mirror of
https://github.com/osukey/osukey.git
synced 2025-08-07 16:43:52 +09:00
Merge branch 'master' into volume-control-acceleration
This commit is contained in:
@ -52,6 +52,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.412.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.412.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.413.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.415.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -7,6 +7,8 @@ using Android.OS;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
|
||||||
namespace osu.Android
|
namespace osu.Android
|
||||||
{
|
{
|
||||||
@ -72,5 +74,14 @@ namespace osu.Android
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
|
protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
|
||||||
|
|
||||||
|
protected override BatteryInfo CreateBatteryInfo() => new AndroidBatteryInfo();
|
||||||
|
|
||||||
|
private class AndroidBatteryInfo : BatteryInfo
|
||||||
|
{
|
||||||
|
public override double ChargeLevel => Battery.ChargeLevel;
|
||||||
|
|
||||||
|
public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,5 +6,6 @@
|
|||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
|
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.BATTERY_STATS" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
|
||||||
</manifest>
|
</manifest>
|
@ -63,5 +63,8 @@
|
|||||||
<Version>5.0.0</Version>
|
<Version>5.0.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override string Name => "Mirror";
|
public override string Name => "Mirror";
|
||||||
public override string Acronym => "MR";
|
public override string Acronym => "MR";
|
||||||
public override ModType Type => ModType.Conversion;
|
public override ModType Type => ModType.Conversion;
|
||||||
|
public override string Description => "Notes are flipped horizontally.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override bool Ranked => true;
|
public override bool Ranked => true;
|
||||||
|
|
||||||
|
44
osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs
Normal file
44
osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// 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 osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject>
|
||||||
|
{
|
||||||
|
[SettingSource("Roll speed", "Rotations per minute")]
|
||||||
|
public BindableNumber<double> SpinSpeed { get; } = new BindableDouble(0.5)
|
||||||
|
{
|
||||||
|
MinValue = 0.02,
|
||||||
|
MaxValue = 4,
|
||||||
|
Precision = 0.01,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Direction", "The direction of rotation")]
|
||||||
|
public Bindable<RotationDirection> Direction { get; } = new Bindable<RotationDirection>(RotationDirection.Clockwise);
|
||||||
|
|
||||||
|
public override string Name => "Barrel Roll";
|
||||||
|
public override string Acronym => "BR";
|
||||||
|
public override string Description => "The whole playfield is on a wheel!";
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
public void Update(Playfield playfield)
|
||||||
|
{
|
||||||
|
playfield.Rotation = (Direction.Value == RotationDirection.CounterClockwise ? -1 : 1) * 360 * (float)(playfield.Time.Current / 60000 * SpinSpeed.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
// scale the playfield to allow all hitobjects to stay within the visible region.
|
||||||
|
drawableRuleset.Playfield.Scale = new Vector2(OsuPlayfield.BASE_SIZE.Y / OsuPlayfield.BASE_SIZE.X);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public override string Name => "Touch Device";
|
public override string Name => "Touch Device";
|
||||||
public override string Acronym => "TD";
|
public override string Acronym => "TD";
|
||||||
|
public override string Description => "Automatically applied to plays on devices with a touchscreen.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override ModType Type => ModType.System;
|
public override ModType Type => ModType.System;
|
||||||
|
@ -185,6 +185,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
new MultiMod(new OsuModGrow(), new OsuModDeflate()),
|
new MultiMod(new OsuModGrow(), new OsuModDeflate()),
|
||||||
new MultiMod(new ModWindUp(), new ModWindDown()),
|
new MultiMod(new ModWindUp(), new ModWindDown()),
|
||||||
new OsuModTraceable(),
|
new OsuModTraceable(),
|
||||||
|
new OsuModBarrelRoll(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.System:
|
case ModType.System:
|
||||||
|
@ -42,6 +42,9 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
public OsuPlayfield()
|
public OsuPlayfield()
|
||||||
{
|
{
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
playfieldBorder = new PlayfieldBorder { RelativeSizeAxes = Axes.Both },
|
playfieldBorder = new PlayfieldBorder { RelativeSizeAxes = Axes.Both },
|
||||||
|
@ -33,7 +33,6 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
Add(cursorScaleContainer = new Container
|
Add(cursorScaleContainer = new Container
|
||||||
{
|
{
|
||||||
RelativePositionAxes = Axes.Both,
|
|
||||||
Child = clickToResumeCursor = new OsuClickToResumeCursor { ResumeRequested = Resume }
|
Child = clickToResumeCursor = new OsuClickToResumeCursor { ResumeRequested = Resume }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
public override string Name => nameof(ModA);
|
public override string Name => nameof(ModA);
|
||||||
public override string Acronym => nameof(ModA);
|
public override string Acronym => nameof(ModA);
|
||||||
|
public override string Description => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) };
|
||||||
@ -152,6 +153,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
private class ModB : Mod
|
private class ModB : Mod
|
||||||
{
|
{
|
||||||
public override string Name => nameof(ModB);
|
public override string Name => nameof(ModB);
|
||||||
|
public override string Description => string.Empty;
|
||||||
public override string Acronym => nameof(ModB);
|
public override string Acronym => nameof(ModB);
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
@ -162,6 +164,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
public override string Name => nameof(ModC);
|
public override string Name => nameof(ModC);
|
||||||
public override string Acronym => nameof(ModC);
|
public override string Acronym => nameof(ModC);
|
||||||
|
public override string Description => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,6 +172,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
public override string Name => $"Incompatible With {nameof(ModA)}";
|
public override string Name => $"Incompatible With {nameof(ModA)}";
|
||||||
public override string Acronym => $"Incompatible With {nameof(ModA)}";
|
public override string Acronym => $"Incompatible With {nameof(ModA)}";
|
||||||
|
public override string Description => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModA) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModA) };
|
||||||
@ -187,6 +191,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
public override string Name => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
|
public override string Name => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
|
||||||
public override string Acronym => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
|
public override string Acronym => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
|
||||||
|
public override string Description => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) };
|
||||||
|
@ -20,27 +20,14 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
handler = new TestInputHandler(replay = new Replay
|
handler = new TestInputHandler(replay = new Replay
|
||||||
{
|
{
|
||||||
Frames = new List<ReplayFrame>
|
HasReceivedAllFrames = false
|
||||||
{
|
|
||||||
new TestReplayFrame(0),
|
|
||||||
new TestReplayFrame(1000),
|
|
||||||
new TestReplayFrame(2000),
|
|
||||||
new TestReplayFrame(3000, true),
|
|
||||||
new TestReplayFrame(4000, true),
|
|
||||||
new TestReplayFrame(5000, true),
|
|
||||||
new TestReplayFrame(7000, true),
|
|
||||||
new TestReplayFrame(8000),
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestNormalPlayback()
|
public void TestNormalPlayback()
|
||||||
{
|
{
|
||||||
Assert.IsNull(handler.CurrentFrame);
|
setReplayFrames();
|
||||||
|
|
||||||
confirmCurrentFrame(null);
|
|
||||||
confirmNextFrame(0);
|
|
||||||
|
|
||||||
setTime(0, 0);
|
setTime(0, 0);
|
||||||
confirmCurrentFrame(0);
|
confirmCurrentFrame(0);
|
||||||
@ -107,6 +94,8 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestIntroTime()
|
public void TestIntroTime()
|
||||||
{
|
{
|
||||||
|
setReplayFrames();
|
||||||
|
|
||||||
setTime(-1000, -1000);
|
setTime(-1000, -1000);
|
||||||
confirmCurrentFrame(null);
|
confirmCurrentFrame(null);
|
||||||
confirmNextFrame(0);
|
confirmNextFrame(0);
|
||||||
@ -123,6 +112,8 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestBasicRewind()
|
public void TestBasicRewind()
|
||||||
{
|
{
|
||||||
|
setReplayFrames();
|
||||||
|
|
||||||
setTime(2800, 0);
|
setTime(2800, 0);
|
||||||
setTime(2800, 1000);
|
setTime(2800, 1000);
|
||||||
setTime(2800, 2000);
|
setTime(2800, 2000);
|
||||||
@ -133,34 +124,35 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
// pivot without crossing a frame boundary
|
// pivot without crossing a frame boundary
|
||||||
setTime(2700, 2700);
|
setTime(2700, 2700);
|
||||||
confirmCurrentFrame(2);
|
confirmCurrentFrame(2);
|
||||||
confirmNextFrame(1);
|
confirmNextFrame(3);
|
||||||
|
|
||||||
// cross current frame boundary; should not yet update frame
|
// cross current frame boundary
|
||||||
setTime(1980, 1980);
|
setTime(1980, 2000);
|
||||||
confirmCurrentFrame(2);
|
confirmCurrentFrame(2);
|
||||||
confirmNextFrame(1);
|
confirmNextFrame(3);
|
||||||
|
|
||||||
setTime(1200, 1200);
|
setTime(1200, 1200);
|
||||||
confirmCurrentFrame(2);
|
confirmCurrentFrame(1);
|
||||||
confirmNextFrame(1);
|
confirmNextFrame(2);
|
||||||
|
|
||||||
// ensure each frame plays out until start
|
// ensure each frame plays out until start
|
||||||
setTime(-500, 1000);
|
setTime(-500, 1000);
|
||||||
confirmCurrentFrame(1);
|
confirmCurrentFrame(1);
|
||||||
confirmNextFrame(0);
|
confirmNextFrame(2);
|
||||||
|
|
||||||
setTime(-500, 0);
|
setTime(-500, 0);
|
||||||
confirmCurrentFrame(0);
|
confirmCurrentFrame(0);
|
||||||
confirmNextFrame(null);
|
confirmNextFrame(1);
|
||||||
|
|
||||||
setTime(-500, -500);
|
setTime(-500, -500);
|
||||||
confirmCurrentFrame(0);
|
confirmCurrentFrame(null);
|
||||||
confirmNextFrame(null);
|
confirmNextFrame(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestRewindInsideImportantSection()
|
public void TestRewindInsideImportantSection()
|
||||||
{
|
{
|
||||||
|
setReplayFrames();
|
||||||
fastForwardToPoint(3000);
|
fastForwardToPoint(3000);
|
||||||
|
|
||||||
setTime(4000, 4000);
|
setTime(4000, 4000);
|
||||||
@ -168,12 +160,12 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
confirmNextFrame(5);
|
confirmNextFrame(5);
|
||||||
|
|
||||||
setTime(3500, null);
|
setTime(3500, null);
|
||||||
confirmCurrentFrame(4);
|
confirmCurrentFrame(3);
|
||||||
confirmNextFrame(3);
|
confirmNextFrame(4);
|
||||||
|
|
||||||
setTime(3000, 3000);
|
setTime(3000, 3000);
|
||||||
confirmCurrentFrame(3);
|
confirmCurrentFrame(3);
|
||||||
confirmNextFrame(2);
|
confirmNextFrame(4);
|
||||||
|
|
||||||
setTime(3500, null);
|
setTime(3500, null);
|
||||||
confirmCurrentFrame(3);
|
confirmCurrentFrame(3);
|
||||||
@ -187,46 +179,127 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
confirmCurrentFrame(4);
|
confirmCurrentFrame(4);
|
||||||
confirmNextFrame(5);
|
confirmNextFrame(5);
|
||||||
|
|
||||||
setTime(4000, null);
|
setTime(4000, 4000);
|
||||||
confirmCurrentFrame(4);
|
confirmCurrentFrame(4);
|
||||||
confirmNextFrame(5);
|
confirmNextFrame(5);
|
||||||
|
|
||||||
setTime(3500, null);
|
setTime(3500, null);
|
||||||
confirmCurrentFrame(4);
|
confirmCurrentFrame(3);
|
||||||
confirmNextFrame(3);
|
confirmNextFrame(4);
|
||||||
|
|
||||||
setTime(3000, 3000);
|
setTime(3000, 3000);
|
||||||
confirmCurrentFrame(3);
|
confirmCurrentFrame(3);
|
||||||
confirmNextFrame(2);
|
confirmNextFrame(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestRewindOutOfImportantSection()
|
public void TestRewindOutOfImportantSection()
|
||||||
{
|
{
|
||||||
|
setReplayFrames();
|
||||||
fastForwardToPoint(3500);
|
fastForwardToPoint(3500);
|
||||||
|
|
||||||
confirmCurrentFrame(3);
|
confirmCurrentFrame(3);
|
||||||
confirmNextFrame(4);
|
confirmNextFrame(4);
|
||||||
|
|
||||||
setTime(3200, null);
|
setTime(3200, null);
|
||||||
// next frame doesn't change even though direction reversed, because of important section.
|
|
||||||
confirmCurrentFrame(3);
|
confirmCurrentFrame(3);
|
||||||
confirmNextFrame(4);
|
confirmNextFrame(4);
|
||||||
|
|
||||||
setTime(3000, null);
|
setTime(3000, 3000);
|
||||||
confirmCurrentFrame(3);
|
confirmCurrentFrame(3);
|
||||||
confirmNextFrame(4);
|
confirmNextFrame(4);
|
||||||
|
|
||||||
setTime(2800, 2800);
|
setTime(2800, 2800);
|
||||||
confirmCurrentFrame(3);
|
confirmCurrentFrame(2);
|
||||||
confirmNextFrame(2);
|
confirmNextFrame(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestReplayStreaming()
|
||||||
|
{
|
||||||
|
// no frames are arrived yet
|
||||||
|
setTime(0, null);
|
||||||
|
setTime(1000, null);
|
||||||
|
Assert.IsTrue(handler.WaitingForFrame, "Should be waiting for the first frame");
|
||||||
|
|
||||||
|
replay.Frames.Add(new TestReplayFrame(0));
|
||||||
|
replay.Frames.Add(new TestReplayFrame(1000));
|
||||||
|
|
||||||
|
// should always play from beginning
|
||||||
|
setTime(1000, 0);
|
||||||
|
confirmCurrentFrame(0);
|
||||||
|
Assert.IsFalse(handler.WaitingForFrame, "Should not be waiting yet");
|
||||||
|
setTime(1000, 1000);
|
||||||
|
confirmCurrentFrame(1);
|
||||||
|
confirmNextFrame(null);
|
||||||
|
Assert.IsTrue(handler.WaitingForFrame, "Should be waiting");
|
||||||
|
|
||||||
|
// cannot seek beyond the last frame
|
||||||
|
setTime(1500, null);
|
||||||
|
confirmCurrentFrame(1);
|
||||||
|
|
||||||
|
setTime(-100, 0);
|
||||||
|
confirmCurrentFrame(0);
|
||||||
|
|
||||||
|
// can seek to the point before the first frame, however
|
||||||
|
setTime(-100, -100);
|
||||||
|
confirmCurrentFrame(null);
|
||||||
|
confirmNextFrame(0);
|
||||||
|
|
||||||
|
fastForwardToPoint(1000);
|
||||||
|
setTime(3000, null);
|
||||||
|
replay.Frames.Add(new TestReplayFrame(2000));
|
||||||
|
confirmCurrentFrame(1);
|
||||||
|
setTime(1000, 1000);
|
||||||
|
setTime(3000, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMultipleFramesSameTime()
|
||||||
|
{
|
||||||
|
replay.Frames.Add(new TestReplayFrame(0));
|
||||||
|
replay.Frames.Add(new TestReplayFrame(0));
|
||||||
|
replay.Frames.Add(new TestReplayFrame(1000));
|
||||||
|
replay.Frames.Add(new TestReplayFrame(1000));
|
||||||
|
replay.Frames.Add(new TestReplayFrame(2000));
|
||||||
|
|
||||||
|
// forward direction is prioritized when multiple frames have the same time.
|
||||||
|
setTime(0, 0);
|
||||||
|
setTime(0, 0);
|
||||||
|
|
||||||
|
setTime(2000, 1000);
|
||||||
|
setTime(2000, 1000);
|
||||||
|
|
||||||
|
setTime(1000, 1000);
|
||||||
|
setTime(1000, 1000);
|
||||||
|
setTime(-100, 1000);
|
||||||
|
setTime(-100, 0);
|
||||||
|
setTime(-100, 0);
|
||||||
|
setTime(-100, -100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setReplayFrames()
|
||||||
|
{
|
||||||
|
replay.Frames = new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new TestReplayFrame(0),
|
||||||
|
new TestReplayFrame(1000),
|
||||||
|
new TestReplayFrame(2000),
|
||||||
|
new TestReplayFrame(3000, true),
|
||||||
|
new TestReplayFrame(4000, true),
|
||||||
|
new TestReplayFrame(5000, true),
|
||||||
|
new TestReplayFrame(7000, true),
|
||||||
|
new TestReplayFrame(8000),
|
||||||
|
};
|
||||||
|
replay.HasReceivedAllFrames = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fastForwardToPoint(double destination)
|
private void fastForwardToPoint(double destination)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 1000; i++)
|
for (int i = 0; i < 1000; i++)
|
||||||
{
|
{
|
||||||
if (handler.SetFrameFromTime(destination) == null)
|
var time = handler.SetFrameFromTime(destination);
|
||||||
|
if (time == null || time == destination)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,33 +308,17 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
|
|
||||||
private void setTime(double set, double? expect)
|
private void setTime(double set, double? expect)
|
||||||
{
|
{
|
||||||
Assert.AreEqual(expect, handler.SetFrameFromTime(set));
|
Assert.AreEqual(expect, handler.SetFrameFromTime(set), "Unexpected return value");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void confirmCurrentFrame(int? frame)
|
private void confirmCurrentFrame(int? frame)
|
||||||
{
|
{
|
||||||
if (frame.HasValue)
|
Assert.AreEqual(frame is int x ? replay.Frames[x].Time : (double?)null, handler.CurrentFrame?.Time, "Unexpected current frame");
|
||||||
{
|
|
||||||
Assert.IsNotNull(handler.CurrentFrame);
|
|
||||||
Assert.AreEqual(replay.Frames[frame.Value].Time, handler.CurrentFrame.Time);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.IsNull(handler.CurrentFrame);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void confirmNextFrame(int? frame)
|
private void confirmNextFrame(int? frame)
|
||||||
{
|
{
|
||||||
if (frame.HasValue)
|
Assert.AreEqual(frame is int x ? replay.Frames[x].Time : (double?)null, handler.NextFrame?.Time, "Unexpected next frame");
|
||||||
{
|
|
||||||
Assert.IsNotNull(handler.NextFrame);
|
|
||||||
Assert.AreEqual(replay.Frames[frame.Value].Time, handler.NextFrame.Time);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.IsNull(handler.NextFrame);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestReplayFrame : ReplayFrame
|
private class TestReplayFrame : ReplayFrame
|
||||||
|
@ -1,296 +0,0 @@
|
|||||||
// 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;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Game.Replays;
|
|
||||||
using osu.Game.Rulesets.Replays;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.NonVisual
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class StreamingFramedReplayInputHandlerTest
|
|
||||||
{
|
|
||||||
private Replay replay;
|
|
||||||
private TestInputHandler handler;
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void SetUp()
|
|
||||||
{
|
|
||||||
handler = new TestInputHandler(replay = new Replay
|
|
||||||
{
|
|
||||||
HasReceivedAllFrames = false,
|
|
||||||
Frames = new List<ReplayFrame>
|
|
||||||
{
|
|
||||||
new TestReplayFrame(0),
|
|
||||||
new TestReplayFrame(1000),
|
|
||||||
new TestReplayFrame(2000),
|
|
||||||
new TestReplayFrame(3000, true),
|
|
||||||
new TestReplayFrame(4000, true),
|
|
||||||
new TestReplayFrame(5000, true),
|
|
||||||
new TestReplayFrame(7000, true),
|
|
||||||
new TestReplayFrame(8000),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestNormalPlayback()
|
|
||||||
{
|
|
||||||
Assert.IsNull(handler.CurrentFrame);
|
|
||||||
|
|
||||||
confirmCurrentFrame(null);
|
|
||||||
confirmNextFrame(0);
|
|
||||||
|
|
||||||
setTime(0, 0);
|
|
||||||
confirmCurrentFrame(0);
|
|
||||||
confirmNextFrame(1);
|
|
||||||
|
|
||||||
// if we hit the first frame perfectly, time should progress to it.
|
|
||||||
setTime(1000, 1000);
|
|
||||||
confirmCurrentFrame(1);
|
|
||||||
confirmNextFrame(2);
|
|
||||||
|
|
||||||
// in between non-important frames should progress based on input.
|
|
||||||
setTime(1200, 1200);
|
|
||||||
confirmCurrentFrame(1);
|
|
||||||
|
|
||||||
setTime(1400, 1400);
|
|
||||||
confirmCurrentFrame(1);
|
|
||||||
|
|
||||||
// progressing beyond the next frame should force time to that frame once.
|
|
||||||
setTime(2200, 2000);
|
|
||||||
confirmCurrentFrame(2);
|
|
||||||
|
|
||||||
// second attempt should progress to input time
|
|
||||||
setTime(2200, 2200);
|
|
||||||
confirmCurrentFrame(2);
|
|
||||||
|
|
||||||
// entering important section
|
|
||||||
setTime(3000, 3000);
|
|
||||||
confirmCurrentFrame(3);
|
|
||||||
|
|
||||||
// cannot progress within
|
|
||||||
setTime(3500, null);
|
|
||||||
confirmCurrentFrame(3);
|
|
||||||
|
|
||||||
setTime(4000, 4000);
|
|
||||||
confirmCurrentFrame(4);
|
|
||||||
|
|
||||||
// still cannot progress
|
|
||||||
setTime(4500, null);
|
|
||||||
confirmCurrentFrame(4);
|
|
||||||
|
|
||||||
setTime(5200, 5000);
|
|
||||||
confirmCurrentFrame(5);
|
|
||||||
|
|
||||||
// important section AllowedImportantTimeSpan allowance
|
|
||||||
setTime(5200, 5200);
|
|
||||||
confirmCurrentFrame(5);
|
|
||||||
|
|
||||||
setTime(7200, 7000);
|
|
||||||
confirmCurrentFrame(6);
|
|
||||||
|
|
||||||
setTime(7200, null);
|
|
||||||
confirmCurrentFrame(6);
|
|
||||||
|
|
||||||
// exited important section
|
|
||||||
setTime(8200, 8000);
|
|
||||||
confirmCurrentFrame(7);
|
|
||||||
confirmNextFrame(null);
|
|
||||||
|
|
||||||
setTime(8200, null);
|
|
||||||
confirmCurrentFrame(7);
|
|
||||||
confirmNextFrame(null);
|
|
||||||
|
|
||||||
setTime(8400, null);
|
|
||||||
confirmCurrentFrame(7);
|
|
||||||
confirmNextFrame(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestIntroTime()
|
|
||||||
{
|
|
||||||
setTime(-1000, -1000);
|
|
||||||
confirmCurrentFrame(null);
|
|
||||||
confirmNextFrame(0);
|
|
||||||
|
|
||||||
setTime(-500, -500);
|
|
||||||
confirmCurrentFrame(null);
|
|
||||||
confirmNextFrame(0);
|
|
||||||
|
|
||||||
setTime(0, 0);
|
|
||||||
confirmCurrentFrame(0);
|
|
||||||
confirmNextFrame(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestBasicRewind()
|
|
||||||
{
|
|
||||||
setTime(2800, 0);
|
|
||||||
setTime(2800, 1000);
|
|
||||||
setTime(2800, 2000);
|
|
||||||
setTime(2800, 2800);
|
|
||||||
confirmCurrentFrame(2);
|
|
||||||
confirmNextFrame(3);
|
|
||||||
|
|
||||||
// pivot without crossing a frame boundary
|
|
||||||
setTime(2700, 2700);
|
|
||||||
confirmCurrentFrame(2);
|
|
||||||
confirmNextFrame(1);
|
|
||||||
|
|
||||||
// cross current frame boundary; should not yet update frame
|
|
||||||
setTime(1980, 1980);
|
|
||||||
confirmCurrentFrame(2);
|
|
||||||
confirmNextFrame(1);
|
|
||||||
|
|
||||||
setTime(1200, 1200);
|
|
||||||
confirmCurrentFrame(2);
|
|
||||||
confirmNextFrame(1);
|
|
||||||
|
|
||||||
// ensure each frame plays out until start
|
|
||||||
setTime(-500, 1000);
|
|
||||||
confirmCurrentFrame(1);
|
|
||||||
confirmNextFrame(0);
|
|
||||||
|
|
||||||
setTime(-500, 0);
|
|
||||||
confirmCurrentFrame(0);
|
|
||||||
confirmNextFrame(null);
|
|
||||||
|
|
||||||
setTime(-500, -500);
|
|
||||||
confirmCurrentFrame(0);
|
|
||||||
confirmNextFrame(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestRewindInsideImportantSection()
|
|
||||||
{
|
|
||||||
fastForwardToPoint(3000);
|
|
||||||
|
|
||||||
setTime(4000, 4000);
|
|
||||||
confirmCurrentFrame(4);
|
|
||||||
confirmNextFrame(5);
|
|
||||||
|
|
||||||
setTime(3500, null);
|
|
||||||
confirmCurrentFrame(4);
|
|
||||||
confirmNextFrame(3);
|
|
||||||
|
|
||||||
setTime(3000, 3000);
|
|
||||||
confirmCurrentFrame(3);
|
|
||||||
confirmNextFrame(2);
|
|
||||||
|
|
||||||
setTime(3500, null);
|
|
||||||
confirmCurrentFrame(3);
|
|
||||||
confirmNextFrame(4);
|
|
||||||
|
|
||||||
setTime(4000, 4000);
|
|
||||||
confirmCurrentFrame(4);
|
|
||||||
confirmNextFrame(5);
|
|
||||||
|
|
||||||
setTime(4500, null);
|
|
||||||
confirmCurrentFrame(4);
|
|
||||||
confirmNextFrame(5);
|
|
||||||
|
|
||||||
setTime(4000, null);
|
|
||||||
confirmCurrentFrame(4);
|
|
||||||
confirmNextFrame(5);
|
|
||||||
|
|
||||||
setTime(3500, null);
|
|
||||||
confirmCurrentFrame(4);
|
|
||||||
confirmNextFrame(3);
|
|
||||||
|
|
||||||
setTime(3000, 3000);
|
|
||||||
confirmCurrentFrame(3);
|
|
||||||
confirmNextFrame(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestRewindOutOfImportantSection()
|
|
||||||
{
|
|
||||||
fastForwardToPoint(3500);
|
|
||||||
|
|
||||||
confirmCurrentFrame(3);
|
|
||||||
confirmNextFrame(4);
|
|
||||||
|
|
||||||
setTime(3200, null);
|
|
||||||
// next frame doesn't change even though direction reversed, because of important section.
|
|
||||||
confirmCurrentFrame(3);
|
|
||||||
confirmNextFrame(4);
|
|
||||||
|
|
||||||
setTime(3000, null);
|
|
||||||
confirmCurrentFrame(3);
|
|
||||||
confirmNextFrame(4);
|
|
||||||
|
|
||||||
setTime(2800, 2800);
|
|
||||||
confirmCurrentFrame(3);
|
|
||||||
confirmNextFrame(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fastForwardToPoint(double destination)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 1000; i++)
|
|
||||||
{
|
|
||||||
if (handler.SetFrameFromTime(destination) == null)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new TimeoutException("Seek was never fulfilled");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setTime(double set, double? expect)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(expect, handler.SetFrameFromTime(set));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void confirmCurrentFrame(int? frame)
|
|
||||||
{
|
|
||||||
if (frame.HasValue)
|
|
||||||
{
|
|
||||||
Assert.IsNotNull(handler.CurrentFrame);
|
|
||||||
Assert.AreEqual(replay.Frames[frame.Value].Time, handler.CurrentFrame.Time);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.IsNull(handler.CurrentFrame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void confirmNextFrame(int? frame)
|
|
||||||
{
|
|
||||||
if (frame.HasValue)
|
|
||||||
{
|
|
||||||
Assert.IsNotNull(handler.NextFrame);
|
|
||||||
Assert.AreEqual(replay.Frames[frame.Value].Time, handler.NextFrame.Time);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.IsNull(handler.NextFrame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestReplayFrame : ReplayFrame
|
|
||||||
{
|
|
||||||
public readonly bool IsImportant;
|
|
||||||
|
|
||||||
public TestReplayFrame(double time, bool isImportant = false)
|
|
||||||
: base(time)
|
|
||||||
{
|
|
||||||
IsImportant = isImportant;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestInputHandler : FramedReplayInputHandler<TestReplayFrame>
|
|
||||||
{
|
|
||||||
public TestInputHandler(Replay replay)
|
|
||||||
: base(replay)
|
|
||||||
{
|
|
||||||
FrameAccuratePlayback = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override double AllowedImportantTimeSpan => 1000;
|
|
||||||
|
|
||||||
protected override bool IsImportant(TestReplayFrame frame) => frame.IsImportant;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -140,6 +140,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
public override string Name => "Test Mod";
|
public override string Name => "Test Mod";
|
||||||
public override string Acronym => "TM";
|
public override string Acronym => "TM";
|
||||||
|
public override string Description => "This is a test mod.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Test")]
|
[SettingSource("Test")]
|
||||||
@ -156,6 +157,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
public override string Name => "Test Mod";
|
public override string Name => "Test Mod";
|
||||||
public override string Acronym => "TMTR";
|
public override string Acronym => "TMTR";
|
||||||
|
public override string Description => "This is a test mod.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||||
|
@ -100,6 +100,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
public override string Name => "Test Mod";
|
public override string Name => "Test Mod";
|
||||||
public override string Acronym => "TM";
|
public override string Acronym => "TM";
|
||||||
|
public override string Description => "This is a test mod.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Test")]
|
[SettingSource("Test")]
|
||||||
@ -116,6 +117,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
public override string Name => "Test Mod";
|
public override string Name => "Test Mod";
|
||||||
public override string Acronym => "TMTR";
|
public override string Acronym => "TMTR";
|
||||||
|
public override string Description => "This is a test mod.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||||
@ -150,6 +152,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
public override string Name => "Test Mod";
|
public override string Name => "Test Mod";
|
||||||
public override string Acronym => "TM";
|
public override string Acronym => "TM";
|
||||||
|
public override string Description => "This is a test mod.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Test")]
|
[SettingSource("Test")]
|
||||||
|
@ -65,6 +65,21 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
stack.Push(songSelect = new DummySongSelect());
|
stack.Push(songSelect = new DummySongSelect());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// User settings should always be ignored on song select screen.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestUserSettingsIgnoredOnSongSelect()
|
||||||
|
{
|
||||||
|
setupUserSettings();
|
||||||
|
AddUntilStep("Screen is undimmed", () => songSelect.IsBackgroundUndimmed());
|
||||||
|
AddUntilStep("Screen using background blur", () => songSelect.IsBackgroundBlur());
|
||||||
|
performFullSetup();
|
||||||
|
AddStep("Exit to song select", () => player.Exit());
|
||||||
|
AddUntilStep("Screen is undimmed", () => songSelect.IsBackgroundUndimmed());
|
||||||
|
AddUntilStep("Screen using background blur", () => songSelect.IsBackgroundBlur());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if <see cref="PlayerLoader"/> properly triggers the visual settings preview when a user hovers over the visual settings panel.
|
/// Check if <see cref="PlayerLoader"/> properly triggers the visual settings preview when a user hovers over the visual settings panel.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -142,9 +157,9 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
{
|
{
|
||||||
performFullSetup();
|
performFullSetup();
|
||||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||||
AddStep("Enable user dim", () => songSelect.DimEnabled.Value = false);
|
AddStep("Disable user dim", () => songSelect.IgnoreUserSettings.Value = true);
|
||||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled());
|
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled());
|
||||||
AddStep("Disable user dim", () => songSelect.DimEnabled.Value = true);
|
AddStep("Enable user dim", () => songSelect.IgnoreUserSettings.Value = false);
|
||||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,13 +176,36 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
player.ReplacesBackground.Value = true;
|
player.ReplacesBackground.Value = true;
|
||||||
player.StoryboardEnabled.Value = true;
|
player.StoryboardEnabled.Value = true;
|
||||||
});
|
});
|
||||||
AddStep("Enable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = true);
|
AddStep("Enable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = false);
|
||||||
AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f);
|
AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f);
|
||||||
AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible);
|
AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible);
|
||||||
AddStep("Disable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = false);
|
AddStep("Disable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = true);
|
||||||
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
|
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestStoryboardIgnoreUserSettings()
|
||||||
|
{
|
||||||
|
performFullSetup();
|
||||||
|
createFakeStoryboard();
|
||||||
|
AddStep("Enable replacing background", () => player.ReplacesBackground.Value = true);
|
||||||
|
|
||||||
|
AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible);
|
||||||
|
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible());
|
||||||
|
|
||||||
|
AddStep("Ignore user settings", () =>
|
||||||
|
{
|
||||||
|
player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true);
|
||||||
|
player.DimmableStoryboard.IgnoreUserSettings.Value = true;
|
||||||
|
});
|
||||||
|
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||||
|
AddUntilStep("Background is invisible", () => songSelect.IsBackgroundInvisible());
|
||||||
|
|
||||||
|
AddStep("Disable background replacement", () => player.ReplacesBackground.Value = false);
|
||||||
|
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||||
|
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if the visual settings container retains dim and blur when pausing
|
/// Check if the visual settings container retains dim and blur when pausing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -204,17 +242,6 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && songSelect.CheckBackgroundBlur(results.ExpectedBackgroundBlur));
|
songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && songSelect.CheckBackgroundBlur(results.ExpectedBackgroundBlur));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if background gets undimmed and unblurred when leaving <see cref="Player"/> for <see cref="PlaySongSelect"/>
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void TestTransitionOut()
|
|
||||||
{
|
|
||||||
performFullSetup();
|
|
||||||
AddStep("Exit to song select", () => player.Exit());
|
|
||||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if hovering on the visual settings dialogue after resuming from player still previews the background dim.
|
/// Check if hovering on the visual settings dialogue after resuming from player still previews the background dim.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -281,11 +308,11 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
protected override BackgroundScreen CreateBackground()
|
protected override BackgroundScreen CreateBackground()
|
||||||
{
|
{
|
||||||
background = new FadeAccessibleBackground(Beatmap.Value);
|
background = new FadeAccessibleBackground(Beatmap.Value);
|
||||||
DimEnabled.BindTo(background.EnableUserDim);
|
IgnoreUserSettings.BindTo(background.IgnoreUserSettings);
|
||||||
return background;
|
return background;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly Bindable<bool> DimEnabled = new Bindable<bool>();
|
public readonly Bindable<bool> IgnoreUserSettings = new Bindable<bool>();
|
||||||
public readonly Bindable<double> DimLevel = new BindableDouble();
|
public readonly Bindable<double> DimLevel = new BindableDouble();
|
||||||
public readonly Bindable<double> BlurLevel = new BindableDouble();
|
public readonly Bindable<double> BlurLevel = new BindableDouble();
|
||||||
|
|
||||||
@ -310,7 +337,7 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
|
|
||||||
public bool IsBackgroundVisible() => background.CurrentAlpha == 1;
|
public bool IsBackgroundVisible() => background.CurrentAlpha == 1;
|
||||||
|
|
||||||
public bool IsBlurCorrect() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR);
|
public bool IsBackgroundBlur() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR);
|
||||||
|
|
||||||
public bool CheckBackgroundBlur(Vector2 expected) => background.CurrentBlur == expected;
|
public bool CheckBackgroundBlur(Vector2 expected) => background.CurrentBlur == expected;
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Components.Timelines.Summary;
|
using osu.Game.Screens.Edit.Components.Timelines.Summary;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -16,18 +15,28 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
public class TestSceneEditorSummaryTimeline : EditorClockTestScene
|
public class TestSceneEditorSummaryTimeline : EditorClockTestScene
|
||||||
{
|
{
|
||||||
[Cached(typeof(EditorBeatmap))]
|
[Cached(typeof(EditorBeatmap))]
|
||||||
private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap());
|
private readonly EditorBeatmap editorBeatmap;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
public TestSceneEditorSummaryTimeline()
|
||||||
private void load()
|
|
||||||
{
|
{
|
||||||
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo));
|
||||||
|
}
|
||||||
|
|
||||||
Add(new SummaryTimeline
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
AddStep("create timeline", () =>
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
// required for track
|
||||||
Origin = Anchor.Centre,
|
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
||||||
Size = new Vector2(500, 50)
|
|
||||||
|
Add(new SummaryTimeline
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(500, 50)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,13 +53,10 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
new AudioVisualiser(),
|
new AudioVisualiser(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TimelineArea = new TimelineArea
|
TimelineArea = new TimelineArea(CreateTestComponent())
|
||||||
{
|
{
|
||||||
Child = CreateTestComponent(),
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Size = new Vector2(0.8f, 100),
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ using osu.Game.Rulesets.Scoring;
|
|||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Play.PlayerSettings;
|
using osu.Game.Screens.Play.PlayerSettings;
|
||||||
|
using osu.Game.Utils;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -48,6 +49,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Cached]
|
[Cached]
|
||||||
private readonly VolumeOverlay volumeOverlay;
|
private readonly VolumeOverlay volumeOverlay;
|
||||||
|
|
||||||
|
[Cached(typeof(BatteryInfo))]
|
||||||
|
private readonly LocalBatteryInfo batteryInfo = new LocalBatteryInfo();
|
||||||
|
|
||||||
private readonly ChangelogOverlay changelogOverlay;
|
private readonly ChangelogOverlay changelogOverlay;
|
||||||
|
|
||||||
public TestScenePlayerLoader()
|
public TestScenePlayerLoader()
|
||||||
@ -288,6 +292,33 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase(false, 1.0, false)] // not charging, above cutoff --> no warning
|
||||||
|
[TestCase(true, 0.1, false)] // charging, below cutoff --> no warning
|
||||||
|
[TestCase(false, 0.25, true)] // not charging, at cutoff --> warning
|
||||||
|
public void TestLowBatteryNotification(bool isCharging, double chargeLevel, bool shouldWarn)
|
||||||
|
{
|
||||||
|
AddStep("reset notification lock", () => sessionStatics.GetBindable<bool>(Static.LowBatteryNotificationShownOnce).Value = false);
|
||||||
|
|
||||||
|
// set charge status and level
|
||||||
|
AddStep("load player", () => resetPlayer(false, () =>
|
||||||
|
{
|
||||||
|
batteryInfo.SetCharging(isCharging);
|
||||||
|
batteryInfo.SetChargeLevel(chargeLevel);
|
||||||
|
}));
|
||||||
|
AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready);
|
||||||
|
AddAssert($"notification {(shouldWarn ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == (shouldWarn ? 1 : 0));
|
||||||
|
AddStep("click notification", () =>
|
||||||
|
{
|
||||||
|
var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last();
|
||||||
|
var flowContainer = scrollContainer.Children.OfType<FillFlowContainer<NotificationSection>>().First();
|
||||||
|
var notification = flowContainer.First();
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(notification);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("wait for player load", () => player.IsLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestEpilepsyWarningEarlyExit()
|
public void TestEpilepsyWarningEarlyExit()
|
||||||
{
|
{
|
||||||
@ -321,6 +352,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public override string Name => string.Empty;
|
public override string Name => string.Empty;
|
||||||
public override string Acronym => string.Empty;
|
public override string Acronym => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
public override string Description => string.Empty;
|
||||||
|
|
||||||
public bool Applied { get; private set; }
|
public bool Applied { get; private set; }
|
||||||
|
|
||||||
@ -348,5 +380,29 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
throw new TimeoutException();
|
throw new TimeoutException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mutable dummy BatteryInfo class for <see cref="TestScenePlayerLoader.TestLowBatteryNotification"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
private class LocalBatteryInfo : BatteryInfo
|
||||||
|
{
|
||||||
|
private bool isCharging = true;
|
||||||
|
private double chargeLevel = 1;
|
||||||
|
|
||||||
|
public override bool IsCharging => isCharging;
|
||||||
|
|
||||||
|
public override double ChargeLevel => chargeLevel;
|
||||||
|
|
||||||
|
public void SetCharging(bool value)
|
||||||
|
{
|
||||||
|
isCharging = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetChargeLevel(double value)
|
||||||
|
{
|
||||||
|
chargeLevel = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
waitForPlayer();
|
waitForPlayer();
|
||||||
AddAssert("ensure frames arrived", () => replayHandler.HasFrames);
|
AddAssert("ensure frames arrived", () => replayHandler.HasFrames);
|
||||||
|
|
||||||
AddUntilStep("wait for frame starvation", () => replayHandler.NextFrame == null);
|
AddUntilStep("wait for frame starvation", () => replayHandler.WaitingForFrame);
|
||||||
checkPaused(true);
|
checkPaused(true);
|
||||||
|
|
||||||
double? pausedTime = null;
|
double? pausedTime = null;
|
||||||
@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
sendFrames();
|
sendFrames();
|
||||||
|
|
||||||
AddUntilStep("wait for frame starvation", () => replayHandler.NextFrame == null);
|
AddUntilStep("wait for frame starvation", () => replayHandler.WaitingForFrame);
|
||||||
checkPaused(true);
|
checkPaused(true);
|
||||||
|
|
||||||
AddAssert("time advanced", () => currentFrameStableTime > pausedTime);
|
AddAssert("time advanced", () => currentFrameStableTime > pausedTime);
|
||||||
|
@ -204,27 +204,27 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (replayHandler.NextFrame != null)
|
if (!replayHandler.HasFrames)
|
||||||
{
|
return;
|
||||||
var lastFrame = replay.Frames.LastOrDefault();
|
|
||||||
|
|
||||||
// this isn't perfect as we basically can't be aware of the rate-of-send here (the streamer is not sending data when not being moved).
|
var lastFrame = replay.Frames.LastOrDefault();
|
||||||
// in gameplay playback, the case where NextFrame is null would pause gameplay and handle this correctly; it's strictly a test limitation / best effort implementation.
|
|
||||||
if (lastFrame != null)
|
|
||||||
latency = Math.Max(latency, Time.Current - lastFrame.Time);
|
|
||||||
|
|
||||||
latencyDisplay.Text = $"latency: {latency:N1}";
|
// this isn't perfect as we basically can't be aware of the rate-of-send here (the streamer is not sending data when not being moved).
|
||||||
|
// in gameplay playback, the case where NextFrame is null would pause gameplay and handle this correctly; it's strictly a test limitation / best effort implementation.
|
||||||
|
if (lastFrame != null)
|
||||||
|
latency = Math.Max(latency, Time.Current - lastFrame.Time);
|
||||||
|
|
||||||
double proposedTime = Time.Current - latency + Time.Elapsed;
|
latencyDisplay.Text = $"latency: {latency:N1}";
|
||||||
|
|
||||||
// this will either advance by one or zero frames.
|
double proposedTime = Time.Current - latency + Time.Elapsed;
|
||||||
double? time = replayHandler.SetFrameFromTime(proposedTime);
|
|
||||||
|
|
||||||
if (time == null)
|
// this will either advance by one or zero frames.
|
||||||
return;
|
double? time = replayHandler.SetFrameFromTime(proposedTime);
|
||||||
|
|
||||||
manualClock.CurrentTime = time.Value;
|
if (time == null)
|
||||||
}
|
return;
|
||||||
|
|
||||||
|
manualClock.CurrentTime = time.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[TearDownSteps]
|
[TearDownSteps]
|
||||||
|
@ -57,6 +57,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
private abstract class TestMod : Mod, IApplicableMod
|
private abstract class TestMod : Mod, IApplicableMod
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.0;
|
public override double ScoreMultiplier => 1.0;
|
||||||
|
|
||||||
|
public override string Description => "This is a test mod.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,6 +226,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.0;
|
public override double ScoreMultiplier => 1.0;
|
||||||
|
|
||||||
|
public override string Description => "This is a customisable test mod.";
|
||||||
|
|
||||||
public override ModType Type => ModType.Conversion;
|
public override ModType Type => ModType.Conversion;
|
||||||
|
|
||||||
[SettingSource("Sample float", "Change something for a mod")]
|
[SettingSource("Sample float", "Change something for a mod")]
|
||||||
|
@ -22,4 +22,4 @@
|
|||||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
MaxValue = 10
|
MaxValue = 10
|
||||||
};
|
};
|
||||||
|
|
||||||
public override Color4 GetRepresentingColour(OsuColour colours) => colours.GreenDark;
|
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Lime1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The speed multiplier at this control point.
|
/// The speed multiplier at this control point.
|
||||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const double default_beat_length = 60000.0 / 60.0;
|
private const double default_beat_length = 60000.0 / 60.0;
|
||||||
|
|
||||||
public override Color4 GetRepresentingColour(OsuColour colours) => colours.YellowDark;
|
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Orange1;
|
||||||
|
|
||||||
public static readonly TimingControlPoint DEFAULT = new TimingControlPoint
|
public static readonly TimingControlPoint DEFAULT = new TimingControlPoint
|
||||||
{
|
{
|
||||||
|
@ -16,6 +16,7 @@ namespace osu.Game.Configuration
|
|||||||
{
|
{
|
||||||
SetDefault(Static.LoginOverlayDisplayed, false);
|
SetDefault(Static.LoginOverlayDisplayed, false);
|
||||||
SetDefault(Static.MutedAudioNotificationShownOnce, false);
|
SetDefault(Static.MutedAudioNotificationShownOnce, false);
|
||||||
|
SetDefault(Static.LowBatteryNotificationShownOnce, false);
|
||||||
SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null);
|
SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null);
|
||||||
SetDefault<APISeasonalBackgrounds>(Static.SeasonalBackgrounds, null);
|
SetDefault<APISeasonalBackgrounds>(Static.SeasonalBackgrounds, null);
|
||||||
}
|
}
|
||||||
@ -25,6 +26,7 @@ namespace osu.Game.Configuration
|
|||||||
{
|
{
|
||||||
LoginOverlayDisplayed,
|
LoginOverlayDisplayed,
|
||||||
MutedAudioNotificationShownOnce,
|
MutedAudioNotificationShownOnce,
|
||||||
|
LowBatteryNotificationShownOnce,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Info about seasonal backgrounds available fetched from API - see <see cref="APISeasonalBackgrounds"/>.
|
/// Info about seasonal backgrounds available fetched from API - see <see cref="APISeasonalBackgrounds"/>.
|
||||||
|
@ -9,6 +9,9 @@ namespace osu.Game.Extensions
|
|||||||
{
|
{
|
||||||
public static class DrawableExtensions
|
public static class DrawableExtensions
|
||||||
{
|
{
|
||||||
|
public const double REPEAT_INTERVAL = 70;
|
||||||
|
public const double INITIAL_DELAY = 250;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper method that is used while <see cref="IKeyBindingHandler"/> doesn't support repetitions of <see cref="IKeyBindingHandler{T}.OnPressed"/>.
|
/// Helper method that is used while <see cref="IKeyBindingHandler"/> doesn't support repetitions of <see cref="IKeyBindingHandler{T}.OnPressed"/>.
|
||||||
/// Simulates repetitions by continually invoking a delegate according to the default key repeat rate.
|
/// Simulates repetitions by continually invoking a delegate according to the default key repeat rate.
|
||||||
@ -19,12 +22,13 @@ namespace osu.Game.Extensions
|
|||||||
/// <param name="handler">The <see cref="IKeyBindingHandler{T}"/> which is handling the repeat.</param>
|
/// <param name="handler">The <see cref="IKeyBindingHandler{T}"/> which is handling the repeat.</param>
|
||||||
/// <param name="scheduler">The <see cref="Scheduler"/> to schedule repetitions on.</param>
|
/// <param name="scheduler">The <see cref="Scheduler"/> to schedule repetitions on.</param>
|
||||||
/// <param name="action">The <see cref="Action"/> to be invoked once immediately and with every repetition.</param>
|
/// <param name="action">The <see cref="Action"/> to be invoked once immediately and with every repetition.</param>
|
||||||
|
/// <param name="initialRepeatDelay">The delay imposed on the first repeat. Defaults to <see cref="INITIAL_DELAY"/>.</param>
|
||||||
/// <returns>A <see cref="ScheduledDelegate"/> which can be cancelled to stop the repeat events from firing.</returns>
|
/// <returns>A <see cref="ScheduledDelegate"/> which can be cancelled to stop the repeat events from firing.</returns>
|
||||||
public static ScheduledDelegate BeginKeyRepeat(this IKeyBindingHandler handler, Scheduler scheduler, Action action)
|
public static ScheduledDelegate BeginKeyRepeat(this IKeyBindingHandler handler, Scheduler scheduler, Action action, double initialRepeatDelay = INITIAL_DELAY)
|
||||||
{
|
{
|
||||||
action();
|
action();
|
||||||
|
|
||||||
ScheduledDelegate repeatDelegate = new ScheduledDelegate(action, handler.Time.Current + 250, 70);
|
ScheduledDelegate repeatDelegate = new ScheduledDelegate(action, handler.Time.Current + initialRepeatDelay, REPEAT_INTERVAL);
|
||||||
scheduler.Add(repeatDelegate);
|
scheduler.Add(repeatDelegate);
|
||||||
return repeatDelegate;
|
return repeatDelegate;
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,6 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
protected const double BACKGROUND_FADE_DURATION = 800;
|
protected const double BACKGROUND_FADE_DURATION = 800;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether or not user-configured dim levels should be applied to the container.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Bindable<bool> EnableUserDim = new Bindable<bool>(true);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not user-configured settings relating to brightness of elements should be ignored
|
/// Whether or not user-configured settings relating to brightness of elements should be ignored
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -57,7 +52,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0;
|
private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0;
|
||||||
|
|
||||||
protected float DimLevel => Math.Max(EnableUserDim.Value && !IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0);
|
protected float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0);
|
||||||
|
|
||||||
protected override Container<Drawable> Content => dimContent;
|
protected override Container<Drawable> Content => dimContent;
|
||||||
|
|
||||||
@ -78,7 +73,6 @@ namespace osu.Game.Graphics.Containers
|
|||||||
LightenDuringBreaks = config.GetBindable<bool>(OsuSetting.LightenDuringBreaks);
|
LightenDuringBreaks = config.GetBindable<bool>(OsuSetting.LightenDuringBreaks);
|
||||||
ShowStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
ShowStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||||
|
|
||||||
EnableUserDim.ValueChanged += _ => UpdateVisuals();
|
|
||||||
UserDimLevel.ValueChanged += _ => UpdateVisuals();
|
UserDimLevel.ValueChanged += _ => UpdateVisuals();
|
||||||
LightenDuringBreaks.ValueChanged += _ => UpdateVisuals();
|
LightenDuringBreaks.ValueChanged += _ => UpdateVisuals();
|
||||||
IsBreakTime.ValueChanged += _ => UpdateVisuals();
|
IsBreakTime.ValueChanged += _ => UpdateVisuals();
|
||||||
|
@ -186,6 +186,13 @@ namespace osu.Game.Graphics
|
|||||||
public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee");
|
public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee");
|
||||||
public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff");
|
public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff");
|
||||||
|
|
||||||
|
// in latest editor design logic, need to figure out where these sit...
|
||||||
|
public readonly Color4 Lime1 = Color4Extensions.FromHex(@"b2ff66");
|
||||||
|
public readonly Color4 Orange1 = Color4Extensions.FromHex(@"ffd966");
|
||||||
|
|
||||||
|
// Content Background
|
||||||
|
public readonly Color4 B5 = Color4Extensions.FromHex(@"222a28");
|
||||||
|
|
||||||
public readonly Color4 RedLighter = Color4Extensions.FromHex(@"ffeded");
|
public readonly Color4 RedLighter = Color4Extensions.FromHex(@"ffeded");
|
||||||
public readonly Color4 RedLight = Color4Extensions.FromHex(@"ed7787");
|
public readonly Color4 RedLight = Color4Extensions.FromHex(@"ed7787");
|
||||||
public readonly Color4 Red = Color4Extensions.FromHex(@"ed1121");
|
public readonly Color4 Red = Color4Extensions.FromHex(@"ed1121");
|
||||||
|
@ -32,8 +32,6 @@ namespace osu.Game.Input.Handlers
|
|||||||
|
|
||||||
public override bool Initialize(GameHost host) => true;
|
public override bool Initialize(GameHost host) => true;
|
||||||
|
|
||||||
public override bool IsActive => true;
|
|
||||||
|
|
||||||
public class ReplayState<T> : IInput
|
public class ReplayState<T> : IInput
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
|
@ -40,6 +40,7 @@ using osu.Game.Rulesets;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Utils;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
using RuntimeInfo = osu.Framework.RuntimeInfo;
|
using RuntimeInfo = osu.Framework.RuntimeInfo;
|
||||||
|
|
||||||
@ -156,6 +157,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager();
|
protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager();
|
||||||
|
|
||||||
|
protected virtual BatteryInfo CreateBatteryInfo() => null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects.
|
/// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -281,6 +284,11 @@ namespace osu.Game
|
|||||||
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
|
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
|
||||||
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
|
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
|
||||||
dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore));
|
dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore));
|
||||||
|
|
||||||
|
var powerStatus = CreateBatteryInfo();
|
||||||
|
if (powerStatus != null)
|
||||||
|
dependencies.CacheAs(powerStatus);
|
||||||
|
|
||||||
dependencies.Cache(new SessionStatics());
|
dependencies.Cache(new SessionStatics());
|
||||||
dependencies.Cache(new OsuColour());
|
dependencies.Cache(new OsuColour());
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Volume
|
namespace osu.Game.Overlays.Volume
|
||||||
@ -15,8 +17,30 @@ namespace osu.Game.Overlays.Volume
|
|||||||
public Func<GlobalAction, bool> ActionRequested;
|
public Func<GlobalAction, bool> ActionRequested;
|
||||||
public Func<GlobalAction, float, bool, bool> ScrollActionRequested;
|
public Func<GlobalAction, float, bool, bool> ScrollActionRequested;
|
||||||
|
|
||||||
public bool OnPressed(GlobalAction action) =>
|
private ScheduledDelegate keyRepeat;
|
||||||
ActionRequested?.Invoke(action) ?? false;
|
|
||||||
|
public bool OnPressed(GlobalAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case GlobalAction.DecreaseVolume:
|
||||||
|
case GlobalAction.IncreaseVolume:
|
||||||
|
keyRepeat?.Cancel();
|
||||||
|
keyRepeat = this.BeginKeyRepeat(Scheduler, () => ActionRequested?.Invoke(action), 150);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case GlobalAction.ToggleMute:
|
||||||
|
ActionRequested?.Invoke(action);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReleased(GlobalAction action)
|
||||||
|
{
|
||||||
|
keyRepeat?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnScroll(ScrollEvent e)
|
protected override bool OnScroll(ScrollEvent e)
|
||||||
{
|
{
|
||||||
@ -27,9 +51,5 @@ namespace osu.Game.Overlays.Volume
|
|||||||
|
|
||||||
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) =>
|
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) =>
|
||||||
ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
|
ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
|
||||||
|
|
||||||
public void OnReleased(GlobalAction action)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
/// The user readable description of this mod.
|
/// The user readable description of this mod.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public virtual string Description => string.Empty;
|
public abstract string Description { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The tooltip to display for this mod when used in a <see cref="ModIcon"/>.
|
/// The tooltip to display for this mod when used in a <see cref="ModIcon"/>.
|
||||||
|
@ -37,8 +37,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
public void ApplyToPlayer(Player player)
|
public void ApplyToPlayer(Player player)
|
||||||
{
|
{
|
||||||
player.ApplyToBackground(b => b.EnableUserDim.Value = false);
|
player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true);
|
||||||
|
|
||||||
player.DimmableStoryboard.IgnoreUserSettings.Value = true;
|
player.DimmableStoryboard.IgnoreUserSettings.Value = true;
|
||||||
|
|
||||||
player.BreakOverlay.Hide();
|
player.BreakOverlay.Hide();
|
||||||
|
@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
{
|
{
|
||||||
public override string Name => "No Mod";
|
public override string Name => "No Mod";
|
||||||
public override string Acronym => "NM";
|
public override string Acronym => "NM";
|
||||||
|
public override string Description => "No mods applied.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.Ban;
|
public override IconUsage? Icon => FontAwesome.Solid.Ban;
|
||||||
public override ModType Type => ModType.System;
|
public override ModType Type => ModType.System;
|
||||||
|
@ -56,8 +56,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
|
|
||||||
public virtual IEnumerable<HitSampleInfo> GetSamples() => HitObject.Samples;
|
public virtual IEnumerable<HitSampleInfo> GetSamples() => HitObject.Samples;
|
||||||
|
|
||||||
private readonly Lazy<List<DrawableHitObject>> nestedHitObjects = new Lazy<List<DrawableHitObject>>();
|
private readonly List<DrawableHitObject> nestedHitObjects = new List<DrawableHitObject>();
|
||||||
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : (IReadOnlyList<DrawableHitObject>)Array.Empty<DrawableHitObject>();
|
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this object should handle any user input events.
|
/// Whether this object should handle any user input events.
|
||||||
@ -249,7 +249,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
// Must be done before the nested DHO is added to occur before the nested Apply()!
|
// Must be done before the nested DHO is added to occur before the nested Apply()!
|
||||||
drawableNested.ParentHitObject = this;
|
drawableNested.ParentHitObject = this;
|
||||||
|
|
||||||
nestedHitObjects.Value.Add(drawableNested);
|
nestedHitObjects.Add(drawableNested);
|
||||||
AddNestedHitObject(drawableNested);
|
AddNestedHitObject(drawableNested);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,19 +305,16 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
if (Samples != null)
|
if (Samples != null)
|
||||||
Samples.Samples = null;
|
Samples.Samples = null;
|
||||||
|
|
||||||
if (nestedHitObjects.IsValueCreated)
|
foreach (var obj in nestedHitObjects)
|
||||||
{
|
{
|
||||||
foreach (var obj in nestedHitObjects.Value)
|
obj.OnNewResult -= onNewResult;
|
||||||
{
|
obj.OnRevertResult -= onRevertResult;
|
||||||
obj.OnNewResult -= onNewResult;
|
obj.ApplyCustomUpdateState -= onApplyCustomUpdateState;
|
||||||
obj.OnRevertResult -= onRevertResult;
|
|
||||||
obj.ApplyCustomUpdateState -= onApplyCustomUpdateState;
|
|
||||||
}
|
|
||||||
|
|
||||||
nestedHitObjects.Value.Clear();
|
|
||||||
ClearNestedHitObjects();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nestedHitObjects.Clear();
|
||||||
|
ClearNestedHitObjects();
|
||||||
|
|
||||||
HitObject.DefaultsApplied -= onDefaultsApplied;
|
HitObject.DefaultsApplied -= onDefaultsApplied;
|
||||||
|
|
||||||
OnFree();
|
OnFree();
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
@ -17,80 +16,92 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
public abstract class FramedReplayInputHandler<TFrame> : ReplayInputHandler
|
public abstract class FramedReplayInputHandler<TFrame> : ReplayInputHandler
|
||||||
where TFrame : ReplayFrame
|
where TFrame : ReplayFrame
|
||||||
{
|
{
|
||||||
private readonly Replay replay;
|
/// <summary>
|
||||||
|
/// Whether we have at least one replay frame.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasFrames => Frames.Count != 0;
|
||||||
|
|
||||||
protected List<ReplayFrame> Frames => replay.Frames;
|
/// <summary>
|
||||||
|
/// Whether we are waiting for new frames to be received.
|
||||||
|
/// </summary>
|
||||||
|
public bool WaitingForFrame => !replay.HasReceivedAllFrames && currentFrameIndex == Frames.Count - 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current frame of the replay.
|
||||||
|
/// The current time is always between the start and the end time of the current frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Returns null if the current time is strictly before the first frame.</remarks>
|
||||||
|
/// <exception cref="InvalidOperationException">The replay is empty.</exception>
|
||||||
public TFrame CurrentFrame
|
public TFrame CurrentFrame
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!HasFrames || !currentFrameIndex.HasValue)
|
if (!HasFrames)
|
||||||
return null;
|
throw new InvalidOperationException($"Attempted to get {nameof(CurrentFrame)} of an empty replay");
|
||||||
|
|
||||||
return (TFrame)Frames[currentFrameIndex.Value];
|
return currentFrameIndex == -1 ? null : (TFrame)Frames[currentFrameIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The next frame of the replay.
|
||||||
|
/// The start time is always greater or equal to the start time of <see cref="CurrentFrame"/> regardless of the seeking direction.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Returns null if the current frame is the last frame.</remarks>
|
||||||
|
/// <exception cref="InvalidOperationException">The replay is empty.</exception>
|
||||||
public TFrame NextFrame
|
public TFrame NextFrame
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!HasFrames)
|
if (!HasFrames)
|
||||||
return null;
|
throw new InvalidOperationException($"Attempted to get {nameof(NextFrame)} of an empty replay");
|
||||||
|
|
||||||
if (!currentFrameIndex.HasValue)
|
return currentFrameIndex == Frames.Count - 1 ? null : (TFrame)Frames[currentFrameIndex + 1];
|
||||||
return currentDirection > 0 ? (TFrame)Frames[0] : null;
|
|
||||||
|
|
||||||
int nextFrame = clampedNextFrameIndex;
|
|
||||||
|
|
||||||
if (nextFrame == currentFrameIndex.Value)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return (TFrame)Frames[clampedNextFrameIndex];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int? currentFrameIndex;
|
|
||||||
|
|
||||||
private int clampedNextFrameIndex =>
|
|
||||||
currentFrameIndex.HasValue ? Math.Clamp(currentFrameIndex.Value + currentDirection, 0, Frames.Count - 1) : 0;
|
|
||||||
|
|
||||||
protected FramedReplayInputHandler(Replay replay)
|
|
||||||
{
|
|
||||||
this.replay = replay;
|
|
||||||
}
|
|
||||||
|
|
||||||
private const double sixty_frame_time = 1000.0 / 60;
|
|
||||||
|
|
||||||
protected virtual double AllowedImportantTimeSpan => sixty_frame_time * 1.2;
|
|
||||||
|
|
||||||
protected double? CurrentTime { get; private set; }
|
|
||||||
|
|
||||||
private int currentDirection = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data.
|
/// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data.
|
||||||
/// Disabling this can make replay playback smoother (useful for autoplay, currently).
|
/// Disabling this can make replay playback smoother (useful for autoplay, currently).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool FrameAccuratePlayback;
|
public bool FrameAccuratePlayback;
|
||||||
|
|
||||||
public bool HasFrames => Frames.Count > 0;
|
// This input handler should be enabled only if there is at least one replay frame.
|
||||||
|
public override bool IsActive => HasFrames;
|
||||||
|
|
||||||
|
// Can make it non-null but that is a breaking change.
|
||||||
|
protected double? CurrentTime { get; private set; }
|
||||||
|
|
||||||
|
protected virtual double AllowedImportantTimeSpan => sixty_frame_time * 1.2;
|
||||||
|
|
||||||
|
protected List<ReplayFrame> Frames => replay.Frames;
|
||||||
|
|
||||||
|
private readonly Replay replay;
|
||||||
|
|
||||||
|
private int currentFrameIndex;
|
||||||
|
|
||||||
|
private const double sixty_frame_time = 1000.0 / 60;
|
||||||
|
|
||||||
|
protected FramedReplayInputHandler(Replay replay)
|
||||||
|
{
|
||||||
|
// TODO: This replay frame ordering should be enforced on the Replay type.
|
||||||
|
// Currently, the ordering can be broken if the frames are added after this construction.
|
||||||
|
replay.Frames.Sort((x, y) => x.Time.CompareTo(y.Time));
|
||||||
|
|
||||||
|
this.replay = replay;
|
||||||
|
currentFrameIndex = -1;
|
||||||
|
CurrentTime = double.NegativeInfinity;
|
||||||
|
}
|
||||||
|
|
||||||
private bool inImportantSection
|
private bool inImportantSection
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!HasFrames || !FrameAccuratePlayback)
|
if (!HasFrames || !FrameAccuratePlayback || CurrentFrame == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var frame = currentDirection > 0 ? CurrentFrame : NextFrame;
|
return IsImportant(CurrentFrame) && // a button is in a pressed state
|
||||||
|
Math.Abs(CurrentTime - NextFrame.Time ?? 0) <= AllowedImportantTimeSpan; // the next frame is within an allowable time span
|
||||||
if (frame == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return IsImportant(frame) && // a button is in a pressed state
|
|
||||||
Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= AllowedImportantTimeSpan; // the next frame is within an allowable time span
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,71 +116,52 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
/// <returns>The usable time value. If null, we should not advance time as we do not have enough data.</returns>
|
/// <returns>The usable time value. If null, we should not advance time as we do not have enough data.</returns>
|
||||||
public override double? SetFrameFromTime(double time)
|
public override double? SetFrameFromTime(double time)
|
||||||
{
|
{
|
||||||
updateDirection(time);
|
|
||||||
|
|
||||||
Debug.Assert(currentDirection != 0);
|
|
||||||
|
|
||||||
if (!HasFrames)
|
if (!HasFrames)
|
||||||
{
|
{
|
||||||
// in the case all frames are received, allow time to progress regardless.
|
// In the case all frames are received, allow time to progress regardless.
|
||||||
if (replay.HasReceivedAllFrames)
|
if (replay.HasReceivedAllFrames)
|
||||||
return CurrentTime = time;
|
return CurrentTime = time;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
TFrame next = NextFrame;
|
double frameStart = getFrameTime(currentFrameIndex);
|
||||||
|
double frameEnd = getFrameTime(currentFrameIndex + 1);
|
||||||
|
|
||||||
// if we have a next frame, check if it is before or at the current time in playback, and advance time to it if so.
|
// If the proposed time is after the current frame end time, we progress forwards to precisely the new frame's time (regardless of incoming time).
|
||||||
if (next != null)
|
if (frameEnd <= time)
|
||||||
{
|
{
|
||||||
int compare = time.CompareTo(next.Time);
|
time = frameEnd;
|
||||||
|
currentFrameIndex++;
|
||||||
if (compare == 0 || compare == currentDirection)
|
|
||||||
{
|
|
||||||
currentFrameIndex = clampedNextFrameIndex;
|
|
||||||
return CurrentTime = CurrentFrame.Time;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// If the proposed time is before the current frame start time, and we are at the frame boundary, we progress backwards.
|
||||||
|
else if (time < frameStart && CurrentTime == frameStart)
|
||||||
|
currentFrameIndex--;
|
||||||
|
|
||||||
// at this point, the frame index can't be advanced.
|
frameStart = getFrameTime(currentFrameIndex);
|
||||||
// even so, we may be able to propose the clock progresses forward due to being at an extent of the replay,
|
frameEnd = getFrameTime(currentFrameIndex + 1);
|
||||||
// or moving towards the next valid frame (ie. interpolating in a non-important section).
|
|
||||||
|
|
||||||
// the exception is if currently in an important section, which is respected above all.
|
// Pause until more frames are arrived.
|
||||||
if (inImportantSection)
|
if (WaitingForFrame && frameStart < time)
|
||||||
{
|
{
|
||||||
Debug.Assert(next != null || !replay.HasReceivedAllFrames);
|
CurrentTime = frameStart;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if a next frame does exist, allow interpolation.
|
CurrentTime = Math.Clamp(time, frameStart, frameEnd);
|
||||||
if (next != null)
|
|
||||||
return CurrentTime = time;
|
|
||||||
|
|
||||||
// if all frames have been received, allow playing beyond extents.
|
// In an important section, a mid-frame time cannot be used and a null is returned instead.
|
||||||
if (replay.HasReceivedAllFrames)
|
return inImportantSection && frameStart < time && time < frameEnd ? null : CurrentTime;
|
||||||
return CurrentTime = time;
|
|
||||||
|
|
||||||
// if not all frames are received but we are before the first frame, allow playing.
|
|
||||||
if (time < Frames[0].Time)
|
|
||||||
return CurrentTime = time;
|
|
||||||
|
|
||||||
// in the case we have no next frames and haven't received enough frame data, block.
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDirection(double time)
|
private double getFrameTime(int index)
|
||||||
{
|
{
|
||||||
if (!CurrentTime.HasValue)
|
if (index < 0)
|
||||||
{
|
return double.NegativeInfinity;
|
||||||
currentDirection = 1;
|
if (index >= Frames.Count)
|
||||||
}
|
return double.PositiveInfinity;
|
||||||
else
|
|
||||||
{
|
return Frames[index].Time;
|
||||||
currentDirection = time.CompareTo(CurrentTime);
|
|
||||||
if (currentDirection == 0) currentDirection = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int JudgedHits { get; private set; }
|
public int JudgedHits { get; private set; }
|
||||||
|
|
||||||
|
private JudgementResult lastAppliedResult;
|
||||||
|
|
||||||
private readonly BindableBool hasCompleted = new BindableBool();
|
private readonly BindableBool hasCompleted = new BindableBool();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -53,12 +55,11 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
public void ApplyResult(JudgementResult result)
|
public void ApplyResult(JudgementResult result)
|
||||||
{
|
{
|
||||||
JudgedHits++;
|
JudgedHits++;
|
||||||
|
lastAppliedResult = result;
|
||||||
|
|
||||||
ApplyResultInternal(result);
|
ApplyResultInternal(result);
|
||||||
|
|
||||||
NewJudgement?.Invoke(result);
|
NewJudgement?.Invoke(result);
|
||||||
|
|
||||||
updateHasCompleted();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -69,8 +70,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
{
|
{
|
||||||
JudgedHits--;
|
JudgedHits--;
|
||||||
|
|
||||||
updateHasCompleted();
|
|
||||||
|
|
||||||
RevertResultInternal(result);
|
RevertResultInternal(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +133,10 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateHasCompleted() => hasCompleted.Value = JudgedHits == MaxHits;
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
hasCompleted.Value = JudgedHits == MaxHits && (JudgedHits == 0 || lastAppliedResult.TimeAbsolute < Clock.CurrentTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
var enumerable = HitObjectContainer.Objects;
|
var enumerable = HitObjectContainer.Objects;
|
||||||
|
|
||||||
if (nestedPlayfields.IsValueCreated)
|
if (nestedPlayfields.Count != 0)
|
||||||
enumerable = enumerable.Concat(NestedPlayfields.SelectMany(p => p.AllHitObjects));
|
enumerable = enumerable.Concat(NestedPlayfields.SelectMany(p => p.AllHitObjects));
|
||||||
|
|
||||||
return enumerable;
|
return enumerable;
|
||||||
@ -76,9 +76,9 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// All <see cref="Playfield"/>s nested inside this <see cref="Playfield"/>.
|
/// All <see cref="Playfield"/>s nested inside this <see cref="Playfield"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<Playfield> NestedPlayfields => nestedPlayfields.IsValueCreated ? nestedPlayfields.Value : Enumerable.Empty<Playfield>();
|
public IEnumerable<Playfield> NestedPlayfields => nestedPlayfields;
|
||||||
|
|
||||||
private readonly Lazy<List<Playfield>> nestedPlayfields = new Lazy<List<Playfield>>();
|
private readonly List<Playfield> nestedPlayfields = new List<Playfield>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether judgements should be displayed by this and and all nested <see cref="Playfield"/>s.
|
/// Whether judgements should be displayed by this and and all nested <see cref="Playfield"/>s.
|
||||||
@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
otherPlayfield.HitObjectUsageBegan += h => HitObjectUsageBegan?.Invoke(h);
|
otherPlayfield.HitObjectUsageBegan += h => HitObjectUsageBegan?.Invoke(h);
|
||||||
otherPlayfield.HitObjectUsageFinished += h => HitObjectUsageFinished?.Invoke(h);
|
otherPlayfield.HitObjectUsageFinished += h => HitObjectUsageFinished?.Invoke(h);
|
||||||
|
|
||||||
nestedPlayfields.Value.Add(otherPlayfield);
|
nestedPlayfields.Add(otherPlayfield);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -279,12 +279,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool removedFromNested = false;
|
return nestedPlayfields.Any(p => p.Remove(hitObject));
|
||||||
|
|
||||||
if (nestedPlayfields.IsValueCreated)
|
|
||||||
removedFromNested = nestedPlayfields.Value.Any(p => p.Remove(hitObject));
|
|
||||||
|
|
||||||
return removedFromNested;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -429,10 +424,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nestedPlayfields.IsValueCreated)
|
foreach (var p in nestedPlayfields)
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var p in nestedPlayfields.Value)
|
|
||||||
p.SetKeepAlive(hitObject, keepAlive);
|
p.SetKeepAlive(hitObject, keepAlive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,10 +436,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
foreach (var (_, entry) in lifetimeEntryMap)
|
foreach (var (_, entry) in lifetimeEntryMap)
|
||||||
entry.KeepAlive = true;
|
entry.KeepAlive = true;
|
||||||
|
|
||||||
if (!nestedPlayfields.IsValueCreated)
|
foreach (var p in nestedPlayfields)
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var p in nestedPlayfields.Value)
|
|
||||||
p.KeepAllAlive();
|
p.KeepAllAlive();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,10 +450,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
HitObjectContainer.PastLifetimeExtension = value;
|
HitObjectContainer.PastLifetimeExtension = value;
|
||||||
|
|
||||||
if (!nestedPlayfields.IsValueCreated)
|
foreach (var nested in nestedPlayfields)
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var nested in nestedPlayfields.Value)
|
|
||||||
nested.PastLifetimeExtension = value;
|
nested.PastLifetimeExtension = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -479,10 +465,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
HitObjectContainer.FutureLifetimeExtension = value;
|
HitObjectContainer.FutureLifetimeExtension = value;
|
||||||
|
|
||||||
if (!nestedPlayfields.IsValueCreated)
|
foreach (var nested in nestedPlayfields)
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var nested in nestedPlayfields.Value)
|
|
||||||
nested.FutureLifetimeExtension = value;
|
nested.FutureLifetimeExtension = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// 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 System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -10,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Input.StateChanges;
|
||||||
using osu.Framework.Input.StateChanges.Events;
|
using osu.Framework.Input.StateChanges.Events;
|
||||||
using osu.Framework.Input.States;
|
using osu.Framework.Input.States;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
@ -100,6 +102,17 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
// to avoid allocation
|
||||||
|
private readonly List<IInput> emptyInputList = new List<IInput>();
|
||||||
|
|
||||||
|
protected override List<IInput> GetPendingInputs()
|
||||||
|
{
|
||||||
|
if (replayInputHandler?.IsActive == false)
|
||||||
|
return emptyInputList;
|
||||||
|
|
||||||
|
return base.GetPendingInputs();
|
||||||
|
}
|
||||||
|
|
||||||
#region Setting application (disables etc.)
|
#region Setting application (disables etc.)
|
||||||
|
|
||||||
private Bindable<bool> mouseDisabled;
|
private Bindable<bool> mouseDisabled;
|
||||||
|
@ -27,9 +27,12 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
private WorkingBeatmap beatmap;
|
private WorkingBeatmap beatmap;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not user dim settings should be applied to this Background.
|
/// Whether or not user-configured settings relating to brightness of elements should be ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Bindable<bool> EnableUserDim = new Bindable<bool>();
|
/// <remarks>
|
||||||
|
/// Beatmap background screens should not apply user settings by default.
|
||||||
|
/// </remarks>
|
||||||
|
public readonly Bindable<bool> IgnoreUserSettings = new Bindable<bool>(true);
|
||||||
|
|
||||||
public readonly Bindable<bool> StoryboardReplacesBackground = new Bindable<bool>();
|
public readonly Bindable<bool> StoryboardReplacesBackground = new Bindable<bool>();
|
||||||
|
|
||||||
@ -50,7 +53,7 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
|
|
||||||
InternalChild = dimmable = CreateFadeContainer();
|
InternalChild = dimmable = CreateFadeContainer();
|
||||||
|
|
||||||
dimmable.EnableUserDim.BindTo(EnableUserDim);
|
dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings);
|
||||||
dimmable.IsBreakTime.BindTo(IsBreakTime);
|
dimmable.IsBreakTime.BindTo(IsBreakTime);
|
||||||
dimmable.BlurAmount.BindTo(BlurAmount);
|
dimmable.BlurAmount.BindTo(BlurAmount);
|
||||||
|
|
||||||
@ -148,7 +151,7 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs.
|
/// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Vector2 blurTarget => EnableUserDim.Value
|
private Vector2 blurTarget => !IgnoreUserSettings.Value
|
||||||
? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * USER_BLUR_FACTOR)
|
? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * USER_BLUR_FACTOR)
|
||||||
: new Vector2(BlurAmount.Value);
|
: new Vector2(BlurAmount.Value);
|
||||||
|
|
||||||
@ -166,7 +169,9 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
BlurAmount.ValueChanged += _ => UpdateVisuals();
|
BlurAmount.ValueChanged += _ => UpdateVisuals();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value; // The background needs to be hidden in the case of it being replaced by the storyboard
|
protected override bool ShowDimContent
|
||||||
|
// The background needs to be hidden in the case of it being replaced by the storyboard
|
||||||
|
=> (!ShowStoryboard.Value && !IgnoreUserSettings.Value) || !StoryboardReplacesBackground.Value;
|
||||||
|
|
||||||
protected override void UpdateVisuals()
|
protected override void UpdateVisuals()
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours) => Colour = colours.Yellow;
|
private void load(OsuColour colours) => Colour = colours.GreyCarmineLight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||||
|
{
|
||||||
|
public class ControlPointVisualisation : PointVisualisation
|
||||||
|
{
|
||||||
|
protected readonly ControlPoint Point;
|
||||||
|
|
||||||
|
public ControlPointVisualisation(ControlPoint point)
|
||||||
|
{
|
||||||
|
Point = point;
|
||||||
|
|
||||||
|
Height = 0.25f;
|
||||||
|
Origin = Anchor.TopCentre;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Colour = Point.GetRepresentingColour(colours);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||||
|
{
|
||||||
|
public class EffectPointVisualisation : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly EffectControlPoint effect;
|
||||||
|
private Bindable<bool> kiai;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private EditorBeatmap beatmap { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
|
public EffectPointVisualisation(EffectControlPoint point)
|
||||||
|
{
|
||||||
|
RelativePositionAxes = Axes.Both;
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
effect = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
kiai = effect.KiaiModeBindable.GetBoundCopy();
|
||||||
|
kiai.BindValueChanged(_ =>
|
||||||
|
{
|
||||||
|
ClearInternal();
|
||||||
|
|
||||||
|
AddInternal(new ControlPointVisualisation(effect));
|
||||||
|
|
||||||
|
if (!kiai.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var endControlPoint = beatmap.ControlPointInfo.EffectPoints.FirstOrDefault(c => c.Time > effect.Time && !c.KiaiMode);
|
||||||
|
|
||||||
|
// handle kiai duration
|
||||||
|
// eventually this will be simpler when we have control points with durations.
|
||||||
|
if (endControlPoint != null)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Origin = Anchor.TopLeft;
|
||||||
|
|
||||||
|
Width = (float)(endControlPoint.Time - effect.Time);
|
||||||
|
|
||||||
|
AddInternal(new PointVisualisation
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Origin = Anchor.TopLeft,
|
||||||
|
Width = 1,
|
||||||
|
Height = 0.25f,
|
||||||
|
Depth = float.MaxValue,
|
||||||
|
Colour = effect.GetRepresentingColour(colours).Darken(0.5f),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +1,33 @@
|
|||||||
// 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 System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||||
{
|
{
|
||||||
public class GroupVisualisation : PointVisualisation
|
public class GroupVisualisation : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
public readonly ControlPointGroup Group;
|
public readonly ControlPointGroup Group;
|
||||||
|
|
||||||
private readonly IBindableList<ControlPoint> controlPoints = new BindableList<ControlPoint>();
|
private readonly IBindableList<ControlPoint> controlPoints = new BindableList<ControlPoint>();
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private OsuColour colours { get; set; }
|
|
||||||
|
|
||||||
public GroupVisualisation(ControlPointGroup group)
|
public GroupVisualisation(ControlPointGroup group)
|
||||||
: base(group.Time)
|
|
||||||
{
|
{
|
||||||
|
RelativePositionAxes = Axes.X;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Origin = Anchor.TopLeft;
|
||||||
|
|
||||||
Group = group;
|
Group = group;
|
||||||
|
X = (float)group.Time;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -33,13 +37,32 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
controlPoints.BindTo(Group.ControlPoints);
|
controlPoints.BindTo(Group.ControlPoints);
|
||||||
controlPoints.BindCollectionChanged((_, __) =>
|
controlPoints.BindCollectionChanged((_, __) =>
|
||||||
{
|
{
|
||||||
if (controlPoints.Count == 0)
|
ClearInternal();
|
||||||
{
|
|
||||||
Colour = Color4.Transparent;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Colour = controlPoints.Any(c => c is TimingControlPoint) ? colours.YellowDark : colours.Green;
|
if (controlPoints.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var point in Group.ControlPoints)
|
||||||
|
{
|
||||||
|
switch (point)
|
||||||
|
{
|
||||||
|
case TimingControlPoint _:
|
||||||
|
AddInternal(new ControlPointVisualisation(point) { Y = 0, });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DifficultyControlPoint _:
|
||||||
|
AddInternal(new ControlPointVisualisation(point) { Y = 0.25f, });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SampleControlPoint _:
|
||||||
|
AddInternal(new ControlPointVisualisation(point) { Y = 0.5f, });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EffectControlPoint effect:
|
||||||
|
AddInternal(new EffectPointVisualisation(effect) { Y = 0.75f });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Y = -10,
|
||||||
Height = 0.35f
|
Height = 0.35f
|
||||||
},
|
},
|
||||||
new BookmarkPart
|
new BookmarkPart
|
||||||
@ -38,6 +39,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
|||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
|
Name = "centre line",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = colours.Gray5,
|
Colour = colours.Gray5,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -45,7 +47,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
|||||||
new Circle
|
new Circle
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.Centre,
|
||||||
Size = new Vector2(5)
|
Size = new Vector2(5)
|
||||||
},
|
},
|
||||||
new Box
|
new Box
|
||||||
@ -59,7 +61,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
|||||||
new Circle
|
new Circle
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.Centre,
|
||||||
Size = new Vector2(5)
|
Size = new Vector2(5)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -69,7 +71,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Height = 0.25f
|
Height = 0.10f
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// 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 osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
|
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
|
||||||
@ -10,19 +9,15 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a spanning point on a timeline part.
|
/// Represents a spanning point on a timeline part.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DurationVisualisation : Container
|
public class DurationVisualisation : Circle
|
||||||
{
|
{
|
||||||
protected DurationVisualisation(double startTime, double endTime)
|
protected DurationVisualisation(double startTime, double endTime)
|
||||||
{
|
{
|
||||||
Masking = true;
|
|
||||||
CornerRadius = 5;
|
|
||||||
|
|
||||||
RelativePositionAxes = Axes.X;
|
RelativePositionAxes = Axes.X;
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
X = (float)startTime;
|
X = (float)startTime;
|
||||||
Width = (float)(endTime - startTime);
|
Width = (float)(endTime - startTime);
|
||||||
|
|
||||||
AddInternal(new Box { RelativeSizeAxes = Axes.Both });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a singular point on a timeline part.
|
/// Represents a singular point on a timeline part.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PointVisualisation : Box
|
public class PointVisualisation : Circle
|
||||||
{
|
{
|
||||||
public const float MAX_WIDTH = 4;
|
public const float MAX_WIDTH = 4;
|
||||||
|
|
||||||
@ -21,9 +21,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
|
|||||||
|
|
||||||
public PointVisualisation()
|
public PointVisualisation()
|
||||||
{
|
{
|
||||||
Origin = Anchor.TopCentre;
|
RelativePositionAxes = Axes.Both;
|
||||||
|
|
||||||
RelativePositionAxes = Axes.X;
|
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
|
||||||
Anchor = Anchor.CentreLeft;
|
Anchor = Anchor.CentreLeft;
|
||||||
|
@ -1,67 +1,27 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
{
|
{
|
||||||
public class DifficultyPointPiece : CompositeDrawable
|
public class DifficultyPointPiece : TopPointPiece
|
||||||
{
|
{
|
||||||
private readonly DifficultyControlPoint difficultyPoint;
|
|
||||||
|
|
||||||
private OsuSpriteText speedMultiplierText;
|
|
||||||
private readonly BindableNumber<double> speedMultiplier;
|
private readonly BindableNumber<double> speedMultiplier;
|
||||||
|
|
||||||
public DifficultyPointPiece(DifficultyControlPoint difficultyPoint)
|
public DifficultyPointPiece(DifficultyControlPoint point)
|
||||||
|
: base(point)
|
||||||
{
|
{
|
||||||
this.difficultyPoint = difficultyPoint;
|
speedMultiplier = point.SpeedMultiplierBindable.GetBoundCopy();
|
||||||
speedMultiplier = difficultyPoint.SpeedMultiplierBindable.GetBoundCopy();
|
|
||||||
|
Y = Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
protected override void LoadComplete()
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y;
|
base.LoadComplete();
|
||||||
AutoSizeAxes = Axes.X;
|
speedMultiplier.BindValueChanged(multiplier => Label.Text = $"{multiplier.NewValue:n2}x", true);
|
||||||
|
|
||||||
Color4 colour = difficultyPoint.GetRepresentingColour(colours);
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Colour = colour,
|
|
||||||
Width = 2,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Colour = colour,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
speedMultiplierText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Font = OsuFont.Default.With(weight: FontWeight.Bold),
|
|
||||||
Colour = Color4.White,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
speedMultiplier.BindValueChanged(multiplier => speedMultiplierText.Text = $"{multiplier.NewValue:n2}x", true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -23,7 +21,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
private readonly BindableNumber<int> volume;
|
private readonly BindableNumber<int> volume;
|
||||||
|
|
||||||
private OsuSpriteText text;
|
private OsuSpriteText text;
|
||||||
private Box volumeBox;
|
private Container volumeBox;
|
||||||
|
|
||||||
|
private const int max_volume_height = 22;
|
||||||
|
|
||||||
public SamplePointPiece(SampleControlPoint samplePoint)
|
public SamplePointPiece(SampleControlPoint samplePoint)
|
||||||
{
|
{
|
||||||
@ -35,8 +35,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
Origin = Anchor.TopLeft;
|
Margin = new MarginPadding { Vertical = 5 };
|
||||||
Anchor = Anchor.TopLeft;
|
|
||||||
|
Origin = Anchor.BottomCentre;
|
||||||
|
Anchor = Anchor.BottomCentre;
|
||||||
|
|
||||||
AutoSizeAxes = Axes.X;
|
AutoSizeAxes = Axes.X;
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
@ -45,40 +47,43 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
|
volumeBox = new Circle
|
||||||
|
{
|
||||||
|
CornerRadius = 5,
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Y = -20,
|
||||||
|
Width = 10,
|
||||||
|
Colour = colour,
|
||||||
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.X,
|
||||||
Width = 20,
|
Height = 16,
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 8,
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
volumeBox = new Box
|
|
||||||
{
|
|
||||||
X = 2,
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Colour = ColourInfo.GradientVertical(colour, Color4.Black),
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
Colour = colour.Lighten(0.2f),
|
Colour = colour,
|
||||||
Width = 2,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
},
|
},
|
||||||
|
text = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Padding = new MarginPadding(5),
|
||||||
|
Font = OsuFont.Default.With(size: 12, weight: FontWeight.SemiBold),
|
||||||
|
Colour = colours.B5,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
text = new OsuSpriteText
|
|
||||||
{
|
|
||||||
X = 2,
|
|
||||||
Y = -5,
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Alpha = 0.9f,
|
|
||||||
Rotation = -90,
|
|
||||||
Font = OsuFont.Default.With(weight: FontWeight.SemiBold)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
volume.BindValueChanged(volume => volumeBox.Height = volume.NewValue / 100f, true);
|
volume.BindValueChanged(volume => volumeBox.Height = max_volume_height * volume.NewValue / 100f, true);
|
||||||
bank.BindValueChanged(bank => text.Text = bank.NewValue, true);
|
bank.BindValueChanged(bank => text.Text = bank.NewValue, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
[Cached]
|
[Cached]
|
||||||
public class Timeline : ZoomableScrollContainer, IPositionSnapProvider
|
public class Timeline : ZoomableScrollContainer, IPositionSnapProvider
|
||||||
{
|
{
|
||||||
|
private readonly Drawable userContent;
|
||||||
public readonly Bindable<bool> WaveformVisible = new Bindable<bool>();
|
public readonly Bindable<bool> WaveformVisible = new Bindable<bool>();
|
||||||
|
|
||||||
public readonly Bindable<bool> ControlPointsVisible = new Bindable<bool>();
|
public readonly Bindable<bool> ControlPointsVisible = new Bindable<bool>();
|
||||||
@ -56,8 +57,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
private Track track;
|
private Track track;
|
||||||
|
|
||||||
public Timeline()
|
private const float timeline_height = 72;
|
||||||
|
private const float timeline_expanded_height = 156;
|
||||||
|
|
||||||
|
public Timeline(Drawable userContent)
|
||||||
{
|
{
|
||||||
|
this.userContent = userContent;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
ZoomDuration = 200;
|
ZoomDuration = 200;
|
||||||
ZoomEasing = Easing.OutQuint;
|
ZoomEasing = Easing.OutQuint;
|
||||||
ScrollbarVisible = false;
|
ScrollbarVisible = false;
|
||||||
@ -69,18 +77,31 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
private TimelineControlPointDisplay controlPoints;
|
private TimelineControlPointDisplay controlPoints;
|
||||||
|
|
||||||
|
private Container mainContent;
|
||||||
|
|
||||||
private Bindable<float> waveformOpacity;
|
private Bindable<float> waveformOpacity;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IBindable<WorkingBeatmap> beatmap, OsuColour colours, OsuConfigManager config)
|
private void load(IBindable<WorkingBeatmap> beatmap, OsuColour colours, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
|
CentreMarker centreMarker;
|
||||||
|
|
||||||
|
// We don't want the centre marker to scroll
|
||||||
|
AddInternal(centreMarker = new CentreMarker());
|
||||||
|
|
||||||
AddRange(new Drawable[]
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
controlPoints = new TimelineControlPointDisplay
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = timeline_expanded_height,
|
||||||
|
},
|
||||||
|
mainContent = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = timeline_height,
|
||||||
Depth = float.MaxValue,
|
Depth = float.MaxValue,
|
||||||
Children = new Drawable[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
waveform = new WaveformGraph
|
waveform = new WaveformGraph
|
||||||
{
|
{
|
||||||
@ -90,8 +111,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
MidColour = colours.BlueDark,
|
MidColour = colours.BlueDark,
|
||||||
HighColour = colours.BlueDarker,
|
HighColour = colours.BlueDarker,
|
||||||
},
|
},
|
||||||
|
centreMarker.CreateProxy(),
|
||||||
ticks = new TimelineTickDisplay(),
|
ticks = new TimelineTickDisplay(),
|
||||||
controlPoints = new TimelineControlPointDisplay(),
|
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
Name = "zero marker",
|
Name = "zero marker",
|
||||||
@ -100,21 +121,43 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
Colour = colours.YellowDarker,
|
Colour = colours.YellowDarker,
|
||||||
},
|
},
|
||||||
|
userContent,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// We don't want the centre marker to scroll
|
|
||||||
AddInternal(new CentreMarker { Depth = float.MaxValue });
|
|
||||||
|
|
||||||
waveformOpacity = config.GetBindable<float>(OsuSetting.EditorWaveformOpacity);
|
waveformOpacity = config.GetBindable<float>(OsuSetting.EditorWaveformOpacity);
|
||||||
|
Beatmap.BindTo(beatmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
waveformOpacity.BindValueChanged(_ => updateWaveformOpacity(), true);
|
waveformOpacity.BindValueChanged(_ => updateWaveformOpacity(), true);
|
||||||
|
|
||||||
WaveformVisible.ValueChanged += _ => updateWaveformOpacity();
|
WaveformVisible.ValueChanged += _ => updateWaveformOpacity();
|
||||||
ControlPointsVisible.ValueChanged += visible => controlPoints.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint);
|
|
||||||
TicksVisible.ValueChanged += visible => ticks.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint);
|
TicksVisible.ValueChanged += visible => ticks.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint);
|
||||||
|
ControlPointsVisible.BindValueChanged(visible =>
|
||||||
|
{
|
||||||
|
if (visible.NewValue)
|
||||||
|
{
|
||||||
|
this.ResizeHeightTo(timeline_expanded_height, 200, Easing.OutQuint);
|
||||||
|
mainContent.MoveToY(36, 200, Easing.OutQuint);
|
||||||
|
|
||||||
|
// delay the fade in else masking looks weird.
|
||||||
|
controlPoints.Delay(180).FadeIn(400, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
controlPoints.FadeOut(200, Easing.OutQuint);
|
||||||
|
|
||||||
|
// likewise, delay the resize until the fade is complete.
|
||||||
|
this.Delay(180).ResizeHeightTo(timeline_height, 200, Easing.OutQuint);
|
||||||
|
mainContent.Delay(180).MoveToY(0, 200, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
Beatmap.BindTo(beatmap);
|
|
||||||
Beatmap.BindValueChanged(b =>
|
Beatmap.BindValueChanged(b =>
|
||||||
{
|
{
|
||||||
waveform.Waveform = b.NewValue.Waveform;
|
waveform.Waveform = b.NewValue.Waveform;
|
||||||
|
@ -12,11 +12,19 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
{
|
{
|
||||||
public class TimelineArea : Container
|
public class TimelineArea : CompositeDrawable
|
||||||
{
|
{
|
||||||
public readonly Timeline Timeline = new Timeline { RelativeSizeAxes = Axes.Both };
|
public Timeline Timeline;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => Timeline;
|
private readonly Drawable userContent;
|
||||||
|
|
||||||
|
public TimelineArea(Drawable content = null)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
userContent = content ?? Drawable.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
@ -37,7 +45,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
},
|
},
|
||||||
new GridContainer
|
new GridContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
Content = new[]
|
Content = new[]
|
||||||
{
|
{
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
@ -55,11 +64,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
},
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Width = 160,
|
Width = 160,
|
||||||
Padding = new MarginPadding { Horizontal = 10 },
|
Padding = new MarginPadding(10),
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(0, 4),
|
Spacing = new Vector2(0, 4),
|
||||||
Children = new[]
|
Children = new[]
|
||||||
@ -123,14 +130,18 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Timeline
|
Timeline = new Timeline(userContent),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
},
|
||||||
ColumnDimensions = new[]
|
ColumnDimensions = new[]
|
||||||
{
|
{
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
new Dimension(GridSizeMode.Distributed),
|
new Dimension(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -65,6 +65,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
Colour = Color4.Black,
|
Colour = Color4.Black,
|
||||||
Depth = float.MaxValue,
|
Depth = float.MaxValue,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||||
|
|
||||||
@ -17,11 +16,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
|
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
|
||||||
|
|
||||||
public TimelineControlPointDisplay()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadBeatmap(EditorBeatmap beatmap)
|
protected override void LoadBeatmap(EditorBeatmap beatmap)
|
||||||
{
|
{
|
||||||
base.LoadBeatmap(beatmap);
|
base.LoadBeatmap(beatmap);
|
||||||
|
@ -27,6 +27,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
AutoSizeAxes = Axes.X;
|
AutoSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
Origin = Anchor.TopCentre;
|
||||||
|
|
||||||
X = (float)group.Time;
|
X = (float)group.Time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ExtendableCircle : CompositeDrawable
|
public class ExtendableCircle : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly CircularContainer content;
|
private readonly Circle content;
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => content.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => content.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
@ -354,19 +354,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
public ExtendableCircle()
|
public ExtendableCircle()
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Horizontal = -circle_size / 2f };
|
Padding = new MarginPadding { Horizontal = -circle_size / 2f };
|
||||||
InternalChild = content = new CircularContainer
|
InternalChild = content = new Circle
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Shadow,
|
Type = EdgeEffectType.Shadow,
|
||||||
Radius = 5,
|
Radius = 5,
|
||||||
Colour = Color4.Black.Opacity(0.4f)
|
Colour = Color4.Black.Opacity(0.4f)
|
||||||
},
|
|
||||||
Child = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -3,60 +3,27 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Colour;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
{
|
{
|
||||||
public class TimingPointPiece : CompositeDrawable
|
public class TimingPointPiece : TopPointPiece
|
||||||
{
|
{
|
||||||
private readonly TimingControlPoint point;
|
|
||||||
|
|
||||||
private readonly BindableNumber<double> beatLength;
|
private readonly BindableNumber<double> beatLength;
|
||||||
private OsuSpriteText bpmText;
|
|
||||||
|
|
||||||
public TimingPointPiece(TimingControlPoint point)
|
public TimingPointPiece(TimingControlPoint point)
|
||||||
|
: base(point)
|
||||||
{
|
{
|
||||||
this.point = point;
|
|
||||||
beatLength = point.BeatLengthBindable.GetBoundCopy();
|
beatLength = point.BeatLengthBindable.GetBoundCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
Origin = Anchor.CentreLeft;
|
|
||||||
Anchor = Anchor.CentreLeft;
|
|
||||||
|
|
||||||
AutoSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
Color4 colour = point.GetRepresentingColour(colours);
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Alpha = 0.9f,
|
|
||||||
Colour = ColourInfo.GradientHorizontal(colour, colour.Opacity(0.5f)),
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
bpmText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Alpha = 0.9f,
|
|
||||||
Padding = new MarginPadding(3),
|
|
||||||
Font = OsuFont.Default.With(size: 40)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
beatLength.BindValueChanged(beatLength =>
|
beatLength.BindValueChanged(beatLength =>
|
||||||
{
|
{
|
||||||
bpmText.Text = $"{60000 / beatLength.NewValue:n1} BPM";
|
Label.Text = $"{60000 / beatLength.NewValue:n1} BPM";
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
|
{
|
||||||
|
public class TopPointPiece : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly ControlPoint point;
|
||||||
|
|
||||||
|
protected OsuSpriteText Label { get; private set; }
|
||||||
|
|
||||||
|
public TopPointPiece(ControlPoint point)
|
||||||
|
{
|
||||||
|
this.point = point;
|
||||||
|
AutoSizeAxes = Axes.X;
|
||||||
|
Height = 16;
|
||||||
|
Margin = new MarginPadding(4);
|
||||||
|
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = Height / 2;
|
||||||
|
|
||||||
|
Origin = Anchor.TopCentre;
|
||||||
|
Anchor = Anchor.TopCentre;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = point.GetRepresentingColour(colours),
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
Label = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Padding = new MarginPadding(3),
|
||||||
|
Font = OsuFont.Default.With(size: 14, weight: FontWeight.SemiBold),
|
||||||
|
Colour = colours.B5,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -467,7 +467,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
// todo: temporary. we want to be applying dim using the UserDimContainer eventually.
|
// todo: temporary. we want to be applying dim using the UserDimContainer eventually.
|
||||||
b.FadeColour(Color4.DarkGray, 500);
|
b.FadeColour(Color4.DarkGray, 500);
|
||||||
|
|
||||||
b.EnableUserDim.Value = false;
|
b.IgnoreUserSettings.Value = true;
|
||||||
b.BlurAmount.Value = 0;
|
b.BlurAmount.Value = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,8 +19,6 @@ namespace osu.Game.Screens.Edit
|
|||||||
private const float vertical_margins = 10;
|
private const float vertical_margins = 10;
|
||||||
private const float horizontal_margins = 20;
|
private const float horizontal_margins = 20;
|
||||||
|
|
||||||
private const float timeline_height = 110;
|
|
||||||
|
|
||||||
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||||
|
|
||||||
private Container timelineContainer;
|
private Container timelineContainer;
|
||||||
@ -40,64 +38,87 @@ namespace osu.Game.Screens.Edit
|
|||||||
if (beatDivisor != null)
|
if (beatDivisor != null)
|
||||||
this.beatDivisor.BindTo(beatDivisor);
|
this.beatDivisor.BindTo(beatDivisor);
|
||||||
|
|
||||||
Children = new Drawable[]
|
Child = new GridContainer
|
||||||
{
|
{
|
||||||
mainContent = new Container
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
RowDimensions = new[]
|
||||||
{
|
{
|
||||||
Name = "Main content",
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
RelativeSizeAxes = Axes.Both,
|
new Dimension(),
|
||||||
Padding = new MarginPadding
|
|
||||||
{
|
|
||||||
Horizontal = horizontal_margins,
|
|
||||||
Top = vertical_margins + timeline_height,
|
|
||||||
Bottom = vertical_margins
|
|
||||||
},
|
|
||||||
Child = spinner = new LoadingSpinner(true)
|
|
||||||
{
|
|
||||||
State = { Value = Visibility.Visible },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
new Container
|
Content = new[]
|
||||||
{
|
{
|
||||||
Name = "Timeline",
|
new Drawable[]
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = timeline_height,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4.Black.Opacity(0.5f)
|
|
||||||
},
|
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Timeline content",
|
Name = "Timeline",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.X,
|
||||||
Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins },
|
AutoSizeAxes = Axes.Y,
|
||||||
Child = new GridContainer
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
new Box
|
||||||
Content = new[]
|
|
||||||
{
|
{
|
||||||
new Drawable[]
|
RelativeSizeAxes = Axes.Both,
|
||||||
{
|
Colour = Color4.Black.Opacity(0.5f)
|
||||||
timelineContainer = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Padding = new MarginPadding { Right = 5 },
|
|
||||||
},
|
|
||||||
new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
ColumnDimensions = new[]
|
new Container
|
||||||
{
|
{
|
||||||
new Dimension(),
|
Name = "Timeline content",
|
||||||
new Dimension(GridSizeMode.Absolute, 90),
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins },
|
||||||
|
Child = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
timelineContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding { Right = 5 },
|
||||||
|
},
|
||||||
|
new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
},
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.Absolute, 90),
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
mainContent = new Container
|
||||||
|
{
|
||||||
|
Name = "Main content",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Depth = float.MaxValue,
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Horizontal = horizontal_margins,
|
||||||
|
Top = vertical_margins,
|
||||||
|
Bottom = vertical_margins
|
||||||
},
|
},
|
||||||
}
|
Child = spinner = new LoadingSpinner(true)
|
||||||
}
|
{
|
||||||
},
|
State = { Value = Visibility.Visible },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,14 +133,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
mainContent.Add(content);
|
mainContent.Add(content);
|
||||||
content.FadeInFromZero(300, Easing.OutQuint);
|
content.FadeInFromZero(300, Easing.OutQuint);
|
||||||
|
|
||||||
LoadComponentAsync(new TimelineArea
|
LoadComponentAsync(new TimelineArea(CreateTimelineContent()), t =>
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
CreateTimelineContent(),
|
|
||||||
}
|
|
||||||
}, t =>
|
|
||||||
{
|
{
|
||||||
timelineContainer.Add(t);
|
timelineContainer.Add(t);
|
||||||
OnTimelineLoaded(t);
|
OnTimelineLoaded(t);
|
||||||
|
@ -764,7 +764,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
ApplyToBackground(b =>
|
ApplyToBackground(b =>
|
||||||
{
|
{
|
||||||
b.EnableUserDim.Value = true;
|
b.IgnoreUserSettings.Value = false;
|
||||||
b.BlurAmount.Value = 0;
|
b.BlurAmount.Value = 0;
|
||||||
|
|
||||||
// bind component bindables.
|
// bind component bindables.
|
||||||
@ -913,7 +913,7 @@ namespace osu.Game.Screens.Play
|
|||||||
float fadeOutDuration = instant ? 0 : 250;
|
float fadeOutDuration = instant ? 0 : 250;
|
||||||
this.FadeOut(fadeOutDuration);
|
this.FadeOut(fadeOutDuration);
|
||||||
|
|
||||||
ApplyToBackground(b => b.EnableUserDim.Value = false);
|
ApplyToBackground(b => b.IgnoreUserSettings.Value = true);
|
||||||
storyboardReplacesBackground.Value = false;
|
storyboardReplacesBackground.Value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ using osu.Game.Overlays.Notifications;
|
|||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Screens.Play.PlayerSettings;
|
using osu.Game.Screens.Play.PlayerSettings;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -112,6 +113,9 @@ namespace osu.Game.Screens.Play
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private AudioManager audioManager { get; set; }
|
private AudioManager audioManager { get; set; }
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private BatteryInfo batteryInfo { get; set; }
|
||||||
|
|
||||||
public PlayerLoader(Func<Player> createPlayer)
|
public PlayerLoader(Func<Player> createPlayer)
|
||||||
{
|
{
|
||||||
this.createPlayer = createPlayer;
|
this.createPlayer = createPlayer;
|
||||||
@ -121,6 +125,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private void load(SessionStatics sessionStatics)
|
private void load(SessionStatics sessionStatics)
|
||||||
{
|
{
|
||||||
muteWarningShownOnce = sessionStatics.GetBindable<bool>(Static.MutedAudioNotificationShownOnce);
|
muteWarningShownOnce = sessionStatics.GetBindable<bool>(Static.MutedAudioNotificationShownOnce);
|
||||||
|
batteryWarningShownOnce = sessionStatics.GetBindable<bool>(Static.LowBatteryNotificationShownOnce);
|
||||||
|
|
||||||
InternalChild = (content = new LogoTrackingContainer
|
InternalChild = (content = new LogoTrackingContainer
|
||||||
{
|
{
|
||||||
@ -196,6 +201,7 @@ namespace osu.Game.Screens.Play
|
|||||||
Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, Clock.CurrentTime + 1800, 0));
|
Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, Clock.CurrentTime + 1800, 0));
|
||||||
|
|
||||||
showMuteWarningIfNeeded();
|
showMuteWarningIfNeeded();
|
||||||
|
showBatteryWarningIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnResuming(IScreen last)
|
public override void OnResuming(IScreen last)
|
||||||
@ -229,7 +235,7 @@ namespace osu.Game.Screens.Play
|
|||||||
content.ScaleTo(0.7f, 150, Easing.InQuint);
|
content.ScaleTo(0.7f, 150, Easing.InQuint);
|
||||||
this.FadeOut(150);
|
this.FadeOut(150);
|
||||||
|
|
||||||
ApplyToBackground(b => b.EnableUserDim.Value = false);
|
ApplyToBackground(b => b.IgnoreUserSettings.Value = true);
|
||||||
|
|
||||||
BackgroundBrightnessReduction = false;
|
BackgroundBrightnessReduction = false;
|
||||||
Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment);
|
Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment);
|
||||||
@ -277,7 +283,7 @@ namespace osu.Game.Screens.Play
|
|||||||
// Preview user-defined background dim and blur when hovered on the visual settings panel.
|
// Preview user-defined background dim and blur when hovered on the visual settings panel.
|
||||||
ApplyToBackground(b =>
|
ApplyToBackground(b =>
|
||||||
{
|
{
|
||||||
b.EnableUserDim.Value = true;
|
b.IgnoreUserSettings.Value = false;
|
||||||
b.BlurAmount.Value = 0;
|
b.BlurAmount.Value = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -288,7 +294,7 @@ namespace osu.Game.Screens.Play
|
|||||||
ApplyToBackground(b =>
|
ApplyToBackground(b =>
|
||||||
{
|
{
|
||||||
// Returns background dim and blur to the values specified by PlayerLoader.
|
// Returns background dim and blur to the values specified by PlayerLoader.
|
||||||
b.EnableUserDim.Value = false;
|
b.IgnoreUserSettings.Value = true;
|
||||||
b.BlurAmount.Value = BACKGROUND_BLUR;
|
b.BlurAmount.Value = BACKGROUND_BLUR;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -470,5 +476,48 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Low battery warning
|
||||||
|
|
||||||
|
private Bindable<bool> batteryWarningShownOnce;
|
||||||
|
|
||||||
|
private void showBatteryWarningIfNeeded()
|
||||||
|
{
|
||||||
|
if (batteryInfo == null) return;
|
||||||
|
|
||||||
|
if (!batteryWarningShownOnce.Value)
|
||||||
|
{
|
||||||
|
if (!batteryInfo.IsCharging && batteryInfo.ChargeLevel <= 0.25)
|
||||||
|
{
|
||||||
|
notificationOverlay?.Post(new BatteryWarningNotification());
|
||||||
|
batteryWarningShownOnce.Value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BatteryWarningNotification : SimpleNotification
|
||||||
|
{
|
||||||
|
public override bool IsImportant => true;
|
||||||
|
|
||||||
|
public BatteryWarningNotification()
|
||||||
|
{
|
||||||
|
Text = "Your battery level is low! Charge your device to prevent interruptions during gameplay.";
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours, NotificationOverlay notificationOverlay)
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.Solid.BatteryQuarter;
|
||||||
|
IconBackgound.Colour = colours.RedDark;
|
||||||
|
|
||||||
|
Activated = delegate
|
||||||
|
{
|
||||||
|
notificationOverlay.Hide();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
osu.Game/Utils/BatteryInfo.cs
Normal file
18
osu.Game/Utils/BatteryInfo.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace osu.Game.Utils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to the system's power status.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class BatteryInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The charge level of the battery, from 0 to 1.
|
||||||
|
/// </summary>
|
||||||
|
public abstract double ChargeLevel { get; }
|
||||||
|
|
||||||
|
public abstract bool IsCharging { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.413.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.415.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.412.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.412.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.2.0" />
|
<PackageReference Include="Sentry" Version="3.2.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.1" />
|
<PackageReference Include="SharpCompress" Version="0.28.1" />
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.413.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.415.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.412.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.412.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
@ -93,7 +93,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.413.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.415.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.1" />
|
<PackageReference Include="SharpCompress" Version="0.28.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
@ -5,6 +5,8 @@ using System;
|
|||||||
using Foundation;
|
using Foundation;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
|
||||||
namespace osu.iOS
|
namespace osu.iOS
|
||||||
{
|
{
|
||||||
@ -13,5 +15,14 @@ namespace osu.iOS
|
|||||||
public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString());
|
public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString());
|
||||||
|
|
||||||
protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
|
protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
|
||||||
|
|
||||||
|
protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo();
|
||||||
|
|
||||||
|
private class IOSBatteryInfo : BatteryInfo
|
||||||
|
{
|
||||||
|
public override double ChargeLevel => Battery.ChargeLevel;
|
||||||
|
|
||||||
|
public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,5 +116,8 @@
|
|||||||
<Visible>false</Visible>
|
<Visible>false</Visible>
|
||||||
</ImageAsset>
|
</ImageAsset>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
|
Reference in New Issue
Block a user