1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 09:27:29 +08:00

Merge branch 'master' into tourney-asset-refactor

This commit is contained in:
Shivam 2020-06-28 15:06:34 +02:00
commit 9639ebdd97
76 changed files with 914 additions and 305 deletions

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="CatchRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<configuration default="false" name="CatchRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@ -15,7 +15,7 @@
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />
<option name="Build" />
</method>
</configuration>
</component>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ManiaRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<configuration default="false" name="ManiaRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Mania.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@ -15,7 +15,7 @@
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />
<option name="Build" />
</method>
</configuration>
</component>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="OsuRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<configuration default="false" name="OsuRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Osu.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@ -15,7 +15,7 @@
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />
<option name="Build" />
</method>
</configuration>
</component>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="TaikoRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<configuration default="false" name="TaikoRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp3.1/osu.Game.Rulesets.Taiko.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@ -15,7 +15,7 @@
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />
<option name="Build" />
</method>
</configuration>
</component>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tournament" type="DotNetProject" factoryName=".NET Project" folderName="Tournament">
<configuration default="false" name="Tournament" type="DotNetProject" factoryName=".NET Project" folderName="Tournament" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="--tournament" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@ -14,7 +14,7 @@
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" enabled="true" />
<option name="Build" />
</method>
</configuration>
</component>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tournament (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Tournament">
<configuration default="false" name="Tournament (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Tournament" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tournament.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tournament.Tests" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@ -15,7 +15,7 @@
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />
<option name="Build" />
</method>
</configuration>
</component>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu!" type="DotNetProject" factoryName=".NET Project" folderName="osu!">
<configuration default="false" name="osu!" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@ -14,7 +14,7 @@
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" enabled="true" />
<option name="Build" />
</method>
</configuration>
</component>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! SDL" type="DotNetProject" factoryName=".NET Project" folderName="osu!">
<configuration default="false" name="osu! SDL" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="--sdl" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@ -12,9 +12,9 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" enabled="true" />
<option name="Build" />
</method>
</configuration>
</component>

View File

@ -1,8 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="osu!">
<configuration default="false" name="osu! (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp3.1/osu.Game.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
@ -14,7 +14,7 @@
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" enabled="true" />
<option name="Build" />
</method>
</configuration>
</component>

View File

@ -51,7 +51,7 @@
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.602.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.619.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.622.1" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.623.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,10 @@
osu file format v14
[General]
Mode: 3
[TimingPoints]
0,300,4,0,2,100,1,0
[HitObjects]
444,320,1000,5,2,0:0:0:0:

View File

@ -0,0 +1,10 @@
osu file format v14
[General]
Mode: 3
[TimingPoints]
0,300,4,0,2,100,1,0
[HitObjects]
444,320,1000,5,1,0:0:0:0:

View File

@ -0,0 +1,49 @@
// 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.Reflection;
using NUnit.Framework;
using osu.Framework.IO.Stores;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestSceneManiaHitObjectSamples : HitObjectSampleTest
{
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
protected override IResourceStore<byte[]> Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples)));
/// <summary>
/// Tests that when a normal sample bank is used, the normal hitsound will be looked up.
/// </summary>
[Test]
public void TestManiaHitObjectNormalSampleBank()
{
const string expected_sample = "normal-hitnormal2";
SetupSkins(expected_sample, expected_sample);
CreateTestWithBeatmap("mania-hitobject-beatmap-normal-sample-bank.osu");
AssertBeatmapLookup(expected_sample);
}
/// <summary>
/// Tests that when a custom sample bank is used, layered hitsounds are not played
/// (only the sample from the custom bank is looked up).
/// </summary>
[Test]
public void TestManiaHitObjectCustomSampleBank()
{
const string expected_sample = "normal-hitwhistle2";
const string unwanted_sample = "normal-hitnormal2";
SetupSkins(expected_sample, unwanted_sample);
CreateTestWithBeatmap("mania-hitobject-beatmap-custom-sample-bank.osu");
AssertBeatmapLookup(expected_sample);
AssertNoLookup(unwanted_sample);
}
}
}

View File

@ -25,8 +25,10 @@ namespace osu.Game.Rulesets.Mania.Judgements
return 200;
case HitResult.Great:
case HitResult.Perfect:
return 300;
case HitResult.Perfect:
return 320;
}
}
}

View File

@ -9,6 +9,9 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Skinning;
using System.Collections.Generic;
using osu.Framework.Audio.Sample;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Legacy;
namespace osu.Game.Rulesets.Mania.Skinning
{
@ -129,6 +132,15 @@ namespace osu.Game.Rulesets.Mania.Skinning
return this.GetAnimation(filename, true, true);
}
public override SampleChannel GetSample(ISampleInfo sampleInfo)
{
// layered hit sounds never play in mania
if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacySample && legacySample.IsLayered)
return new SampleChannelVirtual();
return Source.GetSample(sampleInfo);
}
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
{
if (lookup is ManiaSkinConfigurationLookup maniaLookup)

View File

@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
if (!drawableRepeat.IsHit)
Child.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out);

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -8,7 +9,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Backgrounds;
using osuTK.Graphics;
using osu.Game.Beatmaps.ControlPoints;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Effects;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
if (!effectPoint.KiaiMode)
return;

View File

@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);

View File

@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.UI
lastObjectHit = result.IsHit;
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
kiaiMode = effectPoint.KiaiMode;
}

View File

@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Taiko.UI
textureAnimation.Seek(0);
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
// assume that if the animation is playing on its own, it's independent from the beat and doesn't need to be touched.
if (textureAnimation.FrameCount == 0 || textureAnimation.IsPlaying)

View File

@ -6,6 +6,7 @@ using osu.Framework.IO.Stores;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Skinning;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Resources;
@ -167,5 +168,64 @@ namespace osu.Game.Tests.Gameplay
AssertBeatmapLookup(expected_sample);
}
/// <summary>
/// Tests that when a custom sample bank is used, both the normal and additional sounds will be looked up.
/// </summary>
[Test]
public void TestHitObjectCustomSampleBank()
{
string[] expectedSamples =
{
"normal-hitnormal2",
"normal-hitwhistle2"
};
SetupSkins(expectedSamples[0], expectedSamples[1]);
CreateTestWithBeatmap("hitobject-beatmap-custom-sample-bank.osu");
AssertBeatmapLookup(expectedSamples[0]);
AssertUserLookup(expectedSamples[1]);
}
/// <summary>
/// Tests that when a custom sample bank is used, but <see cref="GlobalSkinConfiguration.LayeredHitSounds"/> is disabled,
/// only the additional sound will be looked up.
/// </summary>
[Test]
public void TestHitObjectCustomSampleBankWithoutLayered()
{
const string expected_sample = "normal-hitwhistle2";
const string unwanted_sample = "normal-hitnormal2";
SetupSkins(expected_sample, unwanted_sample);
disableLayeredHitSounds();
CreateTestWithBeatmap("hitobject-beatmap-custom-sample-bank.osu");
AssertBeatmapLookup(expected_sample);
AssertNoLookup(unwanted_sample);
}
/// <summary>
/// Tests that when a normal sample bank is used and <see cref="GlobalSkinConfiguration.LayeredHitSounds"/> is disabled,
/// the normal sound will be looked up anyway.
/// </summary>
[Test]
public void TestHitObjectNormalSampleBankWithoutLayered()
{
const string expected_sample = "normal-hitnormal";
SetupSkins(expected_sample, expected_sample);
disableLayeredHitSounds();
CreateTestWithBeatmap("hitobject-beatmap-sample.osu");
AssertBeatmapLookup(expected_sample);
}
private void disableLayeredHitSounds()
=> AddStep("set LayeredHitSounds to false", () => Skin.Configuration.ConfigDictionary[GlobalSkinConfiguration.LayeredHitSounds.ToString()] = "0");
}
}

View File

@ -11,8 +11,10 @@ using osu.Framework.Audio.Sample;
using osu.Framework.IO.Stores;
using osu.Framework.Testing;
using osu.Game.Audio;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osu.Game.Storyboards;
@ -70,6 +72,50 @@ namespace osu.Game.Tests.Gameplay
AddUntilStep("sample playback succeeded", () => sample.LifetimeEnd < double.MaxValue);
}
[TestCase(typeof(OsuModDoubleTime), 1.5)]
[TestCase(typeof(OsuModHalfTime), 0.75)]
[TestCase(typeof(ModWindUp), 1.5)]
[TestCase(typeof(ModWindDown), 0.75)]
[TestCase(typeof(OsuModDoubleTime), 2)]
[TestCase(typeof(OsuModHalfTime), 0.5)]
[TestCase(typeof(ModWindUp), 2)]
[TestCase(typeof(ModWindDown), 0.5)]
public void TestSamplePlaybackWithRateMods(Type expectedMod, double expectedRate)
{
GameplayClockContainer gameplayContainer = null;
TestDrawableStoryboardSample sample = null;
Mod testedMod = Activator.CreateInstance(expectedMod) as Mod;
switch (testedMod)
{
case ModRateAdjust m:
m.SpeedChange.Value = expectedRate;
break;
case ModTimeRamp m:
m.InitialRate.Value = m.FinalRate.Value = expectedRate;
break;
}
AddStep("setup storyboard sample", () =>
{
Beatmap.Value = new TestCustomSkinWorkingBeatmap(new OsuRuleset().RulesetInfo, Audio);
SelectedMods.Value = new[] { testedMod };
Add(gameplayContainer = new GameplayClockContainer(Beatmap.Value, SelectedMods.Value, 0));
gameplayContainer.Add(sample = new TestDrawableStoryboardSample(new StoryboardSampleInfo("test-sample", 1, 1))
{
Clock = gameplayContainer.GameplayClock
});
});
AddStep("start", () => gameplayContainer.Start());
AddAssert("sample playback rate matches mod rates", () => sample.Channel.AggregateFrequency.Value == expectedRate);
}
private class TestSkin : LegacySkin
{
public TestSkin(string resourceName, AudioManager audioManager)
@ -99,5 +145,28 @@ namespace osu.Game.Tests.Gameplay
{
}
}
private class TestCustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
{
private readonly AudioManager audio;
public TestCustomSkinWorkingBeatmap(RulesetInfo ruleset, AudioManager audio)
: base(ruleset, null, audio)
{
this.audio = audio;
}
protected override ISkin GetSkin() => new TestSkin("test-sample", audio);
}
private class TestDrawableStoryboardSample : DrawableStoryboardSample
{
public TestDrawableStoryboardSample(StoryboardSampleInfo sampleInfo)
: base(sampleInfo)
{
}
public new SampleChannel Channel => base.Channel;
}
}
}

View File

@ -0,0 +1,7 @@
osu file format v14
[TimingPoints]
0,300,4,0,2,100,1,0
[HitObjects]
444,320,1000,5,2,0:0:0:0:

View File

@ -0,0 +1,5 @@
[General]
Version: latest
[Colours]
Combo1: 255,255,255,0

View File

@ -108,5 +108,15 @@ namespace osu.Game.Tests.Skins
using (var stream = new LineBufferedReader(resStream))
Assert.That(decoder.Decode(stream).LegacyVersion, Is.EqualTo(1.0m));
}
[Test]
public void TestDecodeColourWithZeroAlpha()
{
var decoder = new LegacySkinDecoder();
using (var resStream = TestResources.OpenResource("skin-zero-alpha-colour.ini"))
using (var stream = new LineBufferedReader(resStream))
Assert.That(decoder.Decode(stream).ComboColours[0].A, Is.EqualTo(1.0f));
}
}
}

View File

@ -174,9 +174,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestExitFromGameplay()
{
AddStep("exit", () => Player.Exit());
confirmPaused();
// an externally triggered exit should immediately exit, skipping all pause logic.
AddStep("exit", () => Player.Exit());
confirmExited();
}

View File

@ -0,0 +1,15 @@
// 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 NUnit.Framework;
using osu.Framework.Screens;
using osu.Game.Screens.Menu;
namespace osu.Game.Tests.Visual.Menus
{
[TestFixture]
public class TestSceneIntroWelcome : IntroTestScene
{
protected override IScreen CreateScreen() => new IntroWelcome();
}
}

View File

@ -17,11 +17,12 @@ using osu.Game.Rulesets;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
using osuTK.Input;
namespace osu.Game.Tests.Visual.SongSelect
{
[TestFixture]
public class TestSceneBeatmapCarousel : OsuTestScene
public class TestSceneBeatmapCarousel : OsuManualInputManagerTestScene
{
private TestBeatmapCarousel carousel;
private RulesetStore rulesets;
@ -39,6 +40,43 @@ namespace osu.Game.Tests.Visual.SongSelect
this.rulesets = rulesets;
}
[Test]
public void TestKeyRepeat()
{
loadBeatmaps();
advanceSelection(false);
AddStep("press down arrow", () => InputManager.PressKey(Key.Down));
BeatmapInfo selection = null;
checkSelectionIterating(true);
AddStep("press up arrow", () => InputManager.PressKey(Key.Up));
checkSelectionIterating(true);
AddStep("release down arrow", () => InputManager.ReleaseKey(Key.Down));
checkSelectionIterating(true);
AddStep("release up arrow", () => InputManager.ReleaseKey(Key.Up));
checkSelectionIterating(false);
void checkSelectionIterating(bool isIterating)
{
for (int i = 0; i < 3; i++)
{
AddStep("store selection", () => selection = carousel.SelectedBeatmap);
if (isIterating)
AddUntilStep("selection changed", () => carousel.SelectedBeatmap != selection);
else
AddUntilStep("selection not changed", () => carousel.SelectedBeatmap == selection);
}
}
}
[Test]
public void TestRecommendedSelection()
{

View File

@ -177,7 +177,7 @@ namespace osu.Game.Tests.Visual.UserInterface
timeSinceLastBeat.Value = TimeSinceLastBeat;
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);

View File

@ -9,7 +9,7 @@ using osu.Game.Tournament.Screens.Gameplay.Components;
namespace osu.Game.Tournament.Tests.Components
{
public class TestSceneMatchScoreDisplay : LadderTestScene
public class TestSceneMatchScoreDisplay : TournamentTestScene
{
[Cached(Type = typeof(MatchIPCInfo))]
private MatchIPCInfo matchInfo = new MatchIPCInfo();

View File

@ -8,12 +8,11 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Tests.Visual;
using osu.Game.Tournament.Components;
namespace osu.Game.Tournament.Tests.Components
{
public class TestSceneTournamentBeatmapPanel : OsuTestScene
public class TestSceneTournamentBeatmapPanel : TournamentTestScene
{
[Resolved]
private IAPIProvider api { get; set; }

View File

@ -1,146 +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.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Tournament.Models;
using osu.Game.Users;
namespace osu.Game.Tournament.Tests
{
[TestFixture]
public abstract class LadderTestScene : TournamentTestScene
{
[Cached]
protected LadderInfo Ladder { get; private set; } = new LadderInfo();
[Resolved]
private RulesetStore rulesetStore { get; set; }
[BackgroundDependencyLoader]
private void load()
{
Ladder.Ruleset.Value ??= rulesetStore.AvailableRulesets.First();
Ruleset.BindTo(Ladder.Ruleset);
}
protected override void LoadComplete()
{
base.LoadComplete();
TournamentMatch match = CreateSampleMatch();
Ladder.Rounds.Add(match.Round.Value);
Ladder.Matches.Add(match);
Ladder.Teams.Add(match.Team1.Value);
Ladder.Teams.Add(match.Team2.Value);
Ladder.CurrentMatch.Value = match;
}
public static TournamentMatch CreateSampleMatch() => new TournamentMatch
{
Team1 =
{
Value = new TournamentTeam
{
FlagName = { Value = "JP" },
FullName = { Value = "Japan" },
LastYearPlacing = { Value = 10 },
Seed = { Value = "Low" },
SeedingResults =
{
new SeedingResult
{
Mod = { Value = "NM" },
Seed = { Value = 10 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 12345672,
Seed = { Value = 24 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 1234567,
Seed = { Value = 12 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 1234567,
Seed = { Value = 16 },
}
}
},
new SeedingResult
{
Mod = { Value = "DT" },
Seed = { Value = 5 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 234567,
Seed = { Value = 3 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 234567,
Seed = { Value = 6 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 234567,
Seed = { Value = 12 },
}
}
}
},
Players =
{
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 12 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 16 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 20 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 30 } } },
}
}
},
Team2 =
{
Value = new TournamentTeam
{
FlagName = { Value = "US" },
FullName = { Value = "United States" },
Players =
{
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
}
}
},
Round =
{
Value = new TournamentRound { Name = { Value = "Quarterfinals" } }
}
};
public static BeatmapInfo CreateSampleBeatmapInfo() =>
new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist", ID = RNG.Next(0, 1000000) } };
}
}

View File

@ -8,7 +8,7 @@ using osu.Game.Tournament.Screens.Editors;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneLadderEditorScreen : LadderTestScene
public class TestSceneLadderEditorScreen : TournamentTestScene
{
[BackgroundDependencyLoader]
private void load()

View File

@ -8,7 +8,7 @@ using osu.Game.Tournament.Screens.Ladder;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneLadderScreen : LadderTestScene
public class TestSceneLadderScreen : TournamentTestScene
{
[BackgroundDependencyLoader]
private void load()

View File

@ -12,7 +12,7 @@ using osu.Game.Tournament.Screens.MapPool;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneMapPoolScreen : LadderTestScene
public class TestSceneMapPoolScreen : TournamentTestScene
{
private MapPoolScreen screen;

View File

@ -5,7 +5,7 @@ using osu.Game.Tournament.Screens.Editors;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneRoundEditorScreen : LadderTestScene
public class TestSceneRoundEditorScreen : TournamentTestScene
{
public TestSceneRoundEditorScreen()
{

View File

@ -7,7 +7,7 @@ using osu.Game.Tournament.Screens.Editors;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneSeedingEditorScreen : LadderTestScene
public class TestSceneSeedingEditorScreen : TournamentTestScene
{
[Cached]
private readonly LadderInfo ladder = new LadderInfo();

View File

@ -8,7 +8,7 @@ using osu.Game.Tournament.Screens.TeamIntro;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneSeedingScreen : LadderTestScene
public class TestSceneSeedingScreen : TournamentTestScene
{
[Cached]
private readonly LadderInfo ladder = new LadderInfo();

View File

@ -5,7 +5,7 @@ using osu.Game.Tournament.Screens.Editors;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneTeamEditorScreen : LadderTestScene
public class TestSceneTeamEditorScreen : TournamentTestScene
{
public TestSceneTeamEditorScreen()
{

View File

@ -9,7 +9,7 @@ using osu.Game.Tournament.Screens.TeamIntro;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneTeamIntroScreen : LadderTestScene
public class TestSceneTeamIntroScreen : TournamentTestScene
{
[Cached]
private readonly LadderInfo ladder = new LadderInfo();

View File

@ -4,25 +4,19 @@
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.TeamWin;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneTeamWinScreen : LadderTestScene
public class TestSceneTeamWinScreen : TournamentTestScene
{
[Cached]
private readonly LadderInfo ladder = new LadderInfo();
[BackgroundDependencyLoader]
private void load()
{
var match = new TournamentMatch();
match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA");
match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN");
var match = Ladder.CurrentMatch.Value;
match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals");
match.Completed.Value = true;
ladder.CurrentMatch.Value = match;
Add(new TeamWinScreen
{

View File

@ -1,13 +1,151 @@
// 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.Platform;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Tests.Visual;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models;
using osu.Game.Users;
namespace osu.Game.Tournament.Tests
{
public abstract class TournamentTestScene : OsuTestScene
{
[Cached]
protected LadderInfo Ladder { get; private set; } = new LadderInfo();
[Resolved]
private RulesetStore rulesetStore { get; set; }
[Cached]
protected MatchIPCInfo IPCInfo { get; private set; } = new MatchIPCInfo();
[BackgroundDependencyLoader]
private void load(Storage storage)
{
Ladder.Ruleset.Value ??= rulesetStore.AvailableRulesets.First();
TournamentMatch match = CreateSampleMatch();
Ladder.Rounds.Add(match.Round.Value);
Ladder.Matches.Add(match);
Ladder.Teams.Add(match.Team1.Value);
Ladder.Teams.Add(match.Team2.Value);
Ladder.CurrentMatch.Value = match;
Ruleset.BindTo(Ladder.Ruleset);
Dependencies.CacheAs(new StableInfo(storage));
}
public static TournamentMatch CreateSampleMatch() => new TournamentMatch
{
Team1 =
{
Value = new TournamentTeam
{
Acronym = { Value = "JPN" },
FlagName = { Value = "JP" },
FullName = { Value = "Japan" },
LastYearPlacing = { Value = 10 },
Seed = { Value = "Low" },
SeedingResults =
{
new SeedingResult
{
Mod = { Value = "NM" },
Seed = { Value = 10 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 12345672,
Seed = { Value = 24 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 1234567,
Seed = { Value = 12 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 1234567,
Seed = { Value = 16 },
}
}
},
new SeedingResult
{
Mod = { Value = "DT" },
Seed = { Value = 5 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 234567,
Seed = { Value = 3 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 234567,
Seed = { Value = 6 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 234567,
Seed = { Value = 12 },
}
}
}
},
Players =
{
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 12 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 16 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 20 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 30 } } },
}
}
},
Team2 =
{
Value = new TournamentTeam
{
Acronym = { Value = "USA" },
FlagName = { Value = "US" },
FullName = { Value = "United States" },
Players =
{
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
}
}
},
Round =
{
Value = new TournamentRound { Name = { Value = "Quarterfinals" } }
}
};
public static BeatmapInfo CreateSampleBeatmapInfo() =>
new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist", ID = RNG.Next(0, 1000000) } };
protected override ITestSceneTestRunner CreateRunner() => new TournamentTestSceneTestRunner();
public class TournamentTestSceneTestRunner : TournamentGameBase, ITestSceneTestRunner

View File

@ -37,7 +37,7 @@ namespace osu.Game.Tournament
public const float STREAM_AREA_WIDTH = 1366;
public const double REQUIRED_WIDTH = TournamentSceneManager.CONTROL_AREA_WIDTH * 2 + TournamentSceneManager.STREAM_AREA_WIDTH;
public const double REQUIRED_WIDTH = CONTROL_AREA_WIDTH * 2 + STREAM_AREA_WIDTH;
[Cached]
private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay();

View File

@ -103,7 +103,12 @@ namespace osu.Game.Beatmaps.Formats
try
{
colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), split.Length == 4 ? byte.Parse(split[3]) : (byte)255);
byte alpha = split.Length == 4 ? byte.Parse(split[3]) : (byte)255;
if (alpha == 0)
alpha = 255;
colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), alpha);
}
catch
{

View File

@ -11,7 +11,7 @@ namespace osu.Game.Beatmaps.Formats
/// </summary>
public static class Parsing
{
public const int MAX_COORDINATE_VALUE = 65536;
public const int MAX_COORDINATE_VALUE = 131072;
public const double MAX_PARSE_VALUE = int.MaxValue;

View File

@ -6,6 +6,7 @@ namespace osu.Game.Configuration
public enum IntroSequence
{
Circles,
Welcome,
Triangles,
Random
}

View File

@ -18,7 +18,7 @@ namespace osu.Game.Graphics.Containers
private TimingControlPoint lastTimingPoint;
/// <summary>
/// The amount of time before a beat we should fire <see cref="OnNewBeat(int, TimingControlPoint, EffectControlPoint, TrackAmplitudes)"/>.
/// The amount of time before a beat we should fire <see cref="OnNewBeat(int, TimingControlPoint, EffectControlPoint, ChannelAmplitudes)"/>.
/// This allows for adding easing to animations that may be synchronised to the beat.
/// </summary>
protected double EarlyActivationMilliseconds;
@ -50,7 +50,6 @@ namespace osu.Game.Graphics.Containers
private TimingControlPoint defaultTiming;
private EffectControlPoint defaultEffect;
private TrackAmplitudes defaultAmplitudes;
protected bool IsBeatSyncedWithTrack { get; private set; }
@ -107,7 +106,7 @@ namespace osu.Game.Graphics.Containers
return;
using (BeginDelayedSequence(-TimeSinceLastBeat, true))
OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? defaultAmplitudes);
OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty);
lastBeat = beatIndex;
lastTimingPoint = timingPoint;
@ -128,16 +127,9 @@ namespace osu.Game.Graphics.Containers
KiaiMode = false,
OmitFirstBarLine = false
};
defaultAmplitudes = new TrackAmplitudes
{
FrequencyAmplitudes = new float[256],
LeftChannel = 0,
RightChannel = 0
};
}
protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
}
}

View File

@ -13,7 +13,6 @@ using JetBrains.Annotations;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Events;
using osuTK.Input;
using osu.Framework.Utils;
namespace osu.Game.Graphics.Cursor
@ -74,17 +73,15 @@ namespace osu.Game.Graphics.Cursor
protected override bool OnMouseDown(MouseDownEvent e)
{
// only trigger animation for main mouse buttons
if (e.Button <= MouseButton.Right)
{
activeCursor.Scale = new Vector2(1);
activeCursor.ScaleTo(0.90f, 800, Easing.OutQuint);
activeCursor.Scale = new Vector2(1);
activeCursor.ScaleTo(0.90f, 800, Easing.OutQuint);
activeCursor.AdditiveLayer.Alpha = 0;
activeCursor.AdditiveLayer.FadeInFromZero(800, Easing.OutQuint);
}
activeCursor.AdditiveLayer.Alpha = 0;
activeCursor.AdditiveLayer.FadeInFromZero(800, Easing.OutQuint);
if (e.Button == MouseButton.Left && cursorRotate.Value)
if (cursorRotate.Value && dragRotationState != DragRotationState.Rotating)
{
// if cursor is already rotating don't reset its rotate origin
dragRotationState = DragRotationState.DragStarted;
positionMouseDown = e.MousePosition;
}
@ -94,17 +91,16 @@ namespace osu.Game.Graphics.Cursor
protected override void OnMouseUp(MouseUpEvent e)
{
if (!e.IsPressed(MouseButton.Left) && !e.IsPressed(MouseButton.Right))
if (!e.HasAnyButtonPressed)
{
activeCursor.AdditiveLayer.FadeOutFromOne(500, Easing.OutQuint);
activeCursor.ScaleTo(1, 500, Easing.OutElastic);
}
if (e.Button == MouseButton.Left)
{
if (dragRotationState == DragRotationState.Rotating)
if (dragRotationState != DragRotationState.NotDragging)
{
activeCursor.RotateTo(0, 600 * (1 + Math.Abs(activeCursor.Rotation / 720)), Easing.OutElasticHalf);
dragRotationState = DragRotationState.NotDragging;
dragRotationState = DragRotationState.NotDragging;
}
}
base.OnMouseUp(e);

View File

@ -147,7 +147,7 @@ namespace osu.Game.Graphics.UserInterface
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
if (!hasSelection)
this.FadeTo(0.7f).FadeTo(0.4f, timingPoint.BeatLength, Easing.InOutSine);

View File

@ -230,7 +230,7 @@ namespace osu.Game.Graphics.UserInterface
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);

View File

@ -0,0 +1,9 @@
// 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.Rulesets.Mods
{
public interface IApplicableToAudio : IApplicableToTrack, IApplicableToSample
{
}
}

View File

@ -0,0 +1,15 @@
// 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.Audio.Sample;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for mods that make adjustments to a sample.
/// </summary>
public interface IApplicableToSample : IApplicableMod
{
void ApplyToSample(SampleChannel sample);
}
}

View File

@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mods
private const int bars_per_segment = 4;
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);

View File

@ -2,12 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
namespace osu.Game.Rulesets.Mods
{
public abstract class ModRateAdjust : Mod, IApplicableToTrack
public abstract class ModRateAdjust : Mod, IApplicableToAudio
{
public abstract BindableNumber<double> SpeedChange { get; }
@ -16,6 +17,11 @@ namespace osu.Game.Rulesets.Mods
track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange);
}
public virtual void ApplyToSample(SampleChannel sample)
{
sample.AddAdjustment(AdjustableProperty.Frequency, SpeedChange);
}
public override string SettingDescription => SpeedChange.IsDefault ? string.Empty : $"{SpeedChange.Value:N2}x";
}
}

View File

@ -10,10 +10,11 @@ using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Objects;
using osu.Framework.Audio.Sample;
namespace osu.Game.Rulesets.Mods
{
public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToBeatmap, IApplicableToTrack
public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToBeatmap, IApplicableToAudio
{
/// <summary>
/// The point in the beatmap at which the final ramping rate should be reached.
@ -58,6 +59,11 @@ namespace osu.Game.Rulesets.Mods
AdjustPitch.TriggerChange();
}
public void ApplyToSample(SampleChannel sample)
{
sample.AddAdjustment(AdjustableProperty.Frequency, SpeedChange);
}
public virtual void ApplyToBeatmap(IBeatmap beatmap)
{
HitObject lastObject = beatmap.HitObjects.LastOrDefault();

View File

@ -12,6 +12,7 @@ using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Utils;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Objects.Legacy
{
@ -356,7 +357,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
Bank = bankInfo.Normal,
Name = HitSampleInfo.HIT_NORMAL,
Volume = bankInfo.Volume,
CustomSampleBank = bankInfo.CustomSampleBank
CustomSampleBank = bankInfo.CustomSampleBank,
// if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample.
// None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds
IsLayered = type != LegacyHitSoundType.None && !type.HasFlag(LegacyHitSoundType.Normal)
}
};
@ -409,7 +413,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
}
internal class LegacyHitSampleInfo : HitSampleInfo
public class LegacyHitSampleInfo : HitSampleInfo
{
private int customSampleBank;
@ -424,6 +428,15 @@ namespace osu.Game.Rulesets.Objects.Legacy
Suffix = value.ToString();
}
}
/// <summary>
/// Whether this hit sample is layered.
/// </summary>
/// <remarks>
/// Layered hit samples are automatically added in all modes (except osu!mania), but can be disabled
/// using the <see cref="GlobalSkinConfiguration.LayeredHitSounds"/> skin config option.
/// </remarks>
public bool IsLayered { get; set; }
}
private class FileHitSampleInfo : LegacyHitSampleInfo

View File

@ -13,6 +13,7 @@ using osu.Framework.Graphics.Primitives;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
@ -82,6 +83,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
case NotifyCollectionChangedAction.Remove:
foreach (var o in args.OldItems)
SelectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect();
break;
}
};
@ -250,6 +252,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
blueprint.Deselected -= onBlueprintDeselected;
SelectionBlueprints.Remove(blueprint);
if (movementBlueprint == blueprint)
finishSelectionMovement();
}
protected virtual void AddBlueprintFor(HitObject hitObject)
@ -320,10 +325,22 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
foreach (var blueprint in SelectionBlueprints)
{
if (blueprint.IsAlive && blueprint.IsPresent && rect.Contains(blueprint.ScreenSpaceSelectionPoint))
blueprint.Select();
else
blueprint.Deselect();
// only run when utmost necessary to avoid unnecessary rect computations.
bool isValidForSelection() => blueprint.IsAlive && blueprint.IsPresent && rect.Contains(blueprint.ScreenSpaceSelectionPoint);
switch (blueprint.State)
{
case SelectionState.NotSelected:
if (isValidForSelection())
blueprint.Select();
break;
case SelectionState.Selected:
// if the editor is playing, we generally don't want to deselect objects even if outside the selection area.
if (!editorClock.IsRunning && !isValidForSelection())
blueprint.Deselect();
break;
}
}
}

View File

@ -53,6 +53,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
}
};
private RectangleF? dragRectangle;
/// <summary>
/// Handle a forwarded mouse event.
/// </summary>
@ -66,15 +68,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
var dragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y);
// We use AABBFloat instead of RectangleF since it handles negative sizes for us
var dragRectangle = dragQuad.AABBFloat;
var rec = dragQuad.AABBFloat;
dragRectangle = rec;
var topLeft = ToLocalSpace(dragRectangle.TopLeft);
var bottomRight = ToLocalSpace(dragRectangle.BottomRight);
var topLeft = ToLocalSpace(rec.TopLeft);
var bottomRight = ToLocalSpace(rec.BottomRight);
Box.Position = topLeft;
Box.Size = bottomRight - topLeft;
PerformSelection?.Invoke(dragRectangle);
return true;
}
@ -93,7 +94,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
}
}
public override void Hide() => State = Visibility.Hidden;
protected override void Update()
{
base.Update();
if (dragRectangle != null)
PerformSelection?.Invoke(dragRectangle.Value);
}
public override void Hide()
{
State = Visibility.Hidden;
dragRectangle = null;
}
public override void Show() => State = Visibility.Visible;

View File

@ -51,6 +51,9 @@ namespace osu.Game.Screens
case IntroSequence.Circles:
return new IntroCircles();
case IntroSequence.Welcome:
return new IntroWelcome();
default:
return new IntroTriangles();
}

View File

@ -6,6 +6,7 @@ using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -15,7 +16,6 @@ using osuTK.Graphics;
using osuTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Graphics.Containers;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
@ -132,7 +132,7 @@ namespace osu.Game.Screens.Menu
private bool rightward;
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);

View File

@ -24,7 +24,7 @@ namespace osu.Game.Screens.Menu
private void load(AudioManager audio)
{
if (MenuVoice.Value)
welcome = audio.Samples.Get(@"welcome");
welcome = audio.Samples.Get(@"Intro/welcome");
}
protected override void LogoArriving(OsuLogo logo, bool resuming)

View File

@ -51,6 +51,8 @@ namespace osu.Game.Screens.Menu
private SampleChannel seeya;
protected virtual string SeeyaSampleName => "Intro/seeya";
private LeasedBindable<WorkingBeatmap> beatmap;
private MainMenu mainMenu;
@ -72,7 +74,7 @@ namespace osu.Game.Screens.Menu
MenuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice);
MenuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
seeya = audio.Samples.Get(@"seeya");
seeya = audio.Samples.Get(SeeyaSampleName);
BeatmapSetInfo setInfo = null;

View File

@ -47,7 +47,7 @@ namespace osu.Game.Screens.Menu
private void load()
{
if (MenuVoice.Value && !UsingThemedIntro)
welcome = audio.Samples.Get(@"welcome");
welcome = audio.Samples.Get(@"Intro/welcome");
}
protected override void LogoArriving(OsuLogo logo, bool resuming)

View File

@ -0,0 +1,150 @@
// 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 osuTK;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Screens;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Screens.Backgrounds;
using osuTK.Graphics;
namespace osu.Game.Screens.Menu
{
public class IntroWelcome : IntroScreen
{
protected override string BeatmapHash => "64e00d7022195959bfa3109d09c2e2276c8f12f486b91fcf6175583e973b48f2";
protected override string BeatmapFile => "welcome.osz";
private const double delay_step_two = 2142;
private SampleChannel welcome;
private SampleChannel pianoReverb;
protected override string SeeyaSampleName => "Intro/Welcome/seeya";
protected override BackgroundScreen CreateBackground() => background = new BackgroundScreenDefault(false)
{
Alpha = 0,
};
private BackgroundScreenDefault background;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
if (MenuVoice.Value)
welcome = audio.Samples.Get(@"Intro/Welcome/welcome");
pianoReverb = audio.Samples.Get(@"Intro/Welcome/welcome_piano");
}
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
if (!resuming)
{
LoadComponentAsync(new WelcomeIntroSequence
{
RelativeSizeAxes = Axes.Both
}, intro =>
{
PrepareMenuLoad();
intro.LogoVisualisation.AddAmplitudeSource(pianoReverb);
AddInternal(intro);
welcome?.Play();
pianoReverb?.Play();
Scheduler.AddDelayed(() =>
{
StartTrack();
const float fade_in_time = 200;
logo.ScaleTo(1);
logo.FadeIn(fade_in_time);
background.FadeIn(fade_in_time);
LoadMenu();
}, delay_step_two);
});
}
}
public override void OnResuming(IScreen last)
{
base.OnResuming(last);
background.FadeOut(100);
}
private class WelcomeIntroSequence : Container
{
private Sprite welcomeText;
private Container scaleContainer;
public LogoVisualisation LogoVisualisation { get; private set; }
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Origin = Anchor.Centre;
Anchor = Anchor.Centre;
Children = new Drawable[]
{
scaleContainer = new Container
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
LogoVisualisation = new LogoVisualisation
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0.5f,
AccentColour = Color4.DarkBlue,
Size = new Vector2(0.96f)
},
new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(480),
Colour = Color4.Black
},
welcomeText = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = textures.Get(@"Intro/Welcome/welcome_text")
},
}
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
using (BeginDelayedSequence(0, true))
{
scaleContainer.ScaleTo(0.9f).ScaleTo(1, delay_step_two).OnComplete(_ => Expire());
scaleContainer.FadeInFromZero(1800);
welcomeText.ScaleTo(new Vector2(1, 0)).ScaleTo(Vector2.One, 400, Easing.Out);
}
}
}
}
}

View File

@ -13,7 +13,11 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Utils;
@ -65,6 +69,11 @@ namespace osu.Game.Screens.Menu
public Color4 AccentColour { get; set; }
/// <summary>
/// The relative movement of bars based on input amplification. Defaults to 1.
/// </summary>
public float Magnitude { get; set; } = 1;
private readonly float[] frequencyAmplitudes = new float[256];
private IShader shader;
@ -76,6 +85,13 @@ namespace osu.Game.Screens.Menu
Blending = BlendingParameters.Additive;
}
private readonly List<IHasAmplitudes> amplitudeSources = new List<IHasAmplitudes>();
public void AddAmplitudeSource(IHasAmplitudes amplitudeSource)
{
amplitudeSources.Add(amplitudeSource);
}
[BackgroundDependencyLoader]
private void load(ShaderManager shaders, IBindable<WorkingBeatmap> beatmap)
{
@ -83,27 +99,28 @@ namespace osu.Game.Screens.Menu
shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED);
}
private readonly float[] temporalAmplitudes = new float[ChannelAmplitudes.AMPLITUDES_SIZE];
private void updateAmplitudes()
{
var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null;
var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null;
var effect = beatmap.Value.BeatmapLoaded && beatmap.Value.TrackLoaded
? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(beatmap.Value.Track.CurrentTime)
: null;
float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes;
for (int i = 0; i < temporalAmplitudes.Length; i++)
temporalAmplitudes[i] = 0;
if (beatmap.Value.TrackLoaded)
addAmplitudesFromSource(beatmap.Value.Track);
foreach (var source in amplitudeSources)
addAmplitudesFromSource(source);
for (int i = 0; i < bars_per_visualiser; i++)
{
if (track?.IsRunning ?? false)
{
float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f);
if (targetAmplitude > frequencyAmplitudes[i])
frequencyAmplitudes[i] = targetAmplitude;
}
else
{
int index = (i + index_change) % bars_per_visualiser;
if (frequencyAmplitudes[index] > frequencyAmplitudes[i])
frequencyAmplitudes[i] = frequencyAmplitudes[index];
}
float targetAmplitude = (temporalAmplitudes[(i + indexOffset) % bars_per_visualiser]) * (effect?.KiaiMode == true ? 1 : 0.5f);
if (targetAmplitude > frequencyAmplitudes[i])
frequencyAmplitudes[i] = targetAmplitude;
}
indexOffset = (indexOffset + index_change) % bars_per_visualiser;
@ -136,6 +153,19 @@ namespace osu.Game.Screens.Menu
protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this);
private void addAmplitudesFromSource([NotNull] IHasAmplitudes source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
var amplitudes = source.CurrentAmplitudes.FrequencyAmplitudes.Span;
for (int i = 0; i < amplitudes.Length; i++)
{
if (i < temporalAmplitudes.Length)
temporalAmplitudes[i] += amplitudes[i];
}
}
private class VisualisationDrawNode : DrawNode
{
protected new LogoVisualisation Source => (LogoVisualisation)base.Source;

View File

@ -3,7 +3,6 @@
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@ -16,6 +15,7 @@ using osu.Game.Skinning;
using osu.Game.Online.API;
using osu.Game.Users;
using System;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
namespace osu.Game.Screens.Menu
@ -89,7 +89,7 @@ namespace osu.Game.Screens.Menu
skin.BindValueChanged(_ => updateColour(), true);
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
if (beatIndex < 0)
return;
@ -100,7 +100,7 @@ namespace osu.Game.Screens.Menu
flash(rightBox, timingPoint.BeatLength, effectPoint.KiaiMode, amplitudes);
}
private void flash(Drawable d, double beatLength, bool kiai, TrackAmplitudes amplitudes)
private void flash(Drawable d, double beatLength, bool kiai, ChannelAmplitudes amplitudes)
{
d.FadeTo(Math.Max(0, ((ReferenceEquals(d, leftBox) ? amplitudes.LeftChannel : amplitudes.RightChannel) - amplitude_dead_zone) / (kiai ? kiai_multiplier : alpha_multiplier)), box_fade_in_time)
.Then()

View File

@ -264,7 +264,7 @@ namespace osu.Game.Screens.Menu
private int lastBeatIndex;
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);

View File

@ -656,12 +656,6 @@ namespace osu.Game.Screens.Play
return true;
}
if (canPause)
{
Pause();
return true;
}
// GameplayClockContainer performs seeks / start / stop operations on the beatmap's track.
// as we are no longer the current screen, we cannot guarantee the track is still usable.
GameplayClockContainer?.StopUsingBeatmapClock();

View File

@ -19,6 +19,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps;
using osu.Game.Extensions;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
using osu.Game.Input.Bindings;
@ -301,6 +302,9 @@ namespace osu.Game.Screens.Select
private void selectNextDifficulty(int direction)
{
if (selectedBeatmap == null)
return;
var unfilteredDifficulties = selectedBeatmapSet.Children.Where(s => !s.Filtered.Value).ToList();
int index = unfilteredDifficulties.IndexOf(selectedBeatmap);
@ -452,32 +456,49 @@ namespace osu.Game.Screens.Select
/// </summary>
public void ScrollToSelected() => scrollPositionCache.Invalidate();
#region Key / button selection logic
protected override bool OnKeyDown(KeyDownEvent e)
{
switch (e.Key)
{
case Key.Left:
SelectNext(-1, true);
if (!e.Repeat)
beginRepeatSelection(() => SelectNext(-1, true), e.Key);
return true;
case Key.Right:
SelectNext(1, true);
if (!e.Repeat)
beginRepeatSelection(() => SelectNext(1, true), e.Key);
return true;
}
return false;
}
protected override void OnKeyUp(KeyUpEvent e)
{
switch (e.Key)
{
case Key.Left:
case Key.Right:
endRepeatSelection(e.Key);
break;
}
base.OnKeyUp(e);
}
public bool OnPressed(GlobalAction action)
{
switch (action)
{
case GlobalAction.SelectNext:
SelectNext(1, false);
beginRepeatSelection(() => SelectNext(1, false), action);
return true;
case GlobalAction.SelectPrevious:
SelectNext(-1, false);
beginRepeatSelection(() => SelectNext(-1, false), action);
return true;
}
@ -486,8 +507,44 @@ namespace osu.Game.Screens.Select
public void OnReleased(GlobalAction action)
{
switch (action)
{
case GlobalAction.SelectNext:
case GlobalAction.SelectPrevious:
endRepeatSelection(action);
break;
}
}
private ScheduledDelegate repeatDelegate;
private object lastRepeatSource;
/// <summary>
/// Begin repeating the specified selection action.
/// </summary>
/// <param name="action">The action to perform.</param>
/// <param name="source">The source of the action. Used in conjunction with <see cref="endRepeatSelection"/> to only cancel the correct action (most recently pressed key).</param>
private void beginRepeatSelection(Action action, object source)
{
endRepeatSelection();
lastRepeatSource = source;
repeatDelegate = this.BeginKeyRepeat(Scheduler, action);
}
private void endRepeatSelection(object source = null)
{
// only the most recent source should be able to cancel the current action.
if (source != null && !EqualityComparer<object>.Default.Equals(lastRepeatSource, source))
return;
repeatDelegate?.Cancel();
repeatDelegate = null;
lastRepeatSource = null;
}
#endregion
protected override void Update()
{
base.Update();

View File

@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select
public ImportFromStablePopup(Action importFromStable)
{
HeaderText = @"You have no beatmaps!";
BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps, skins and scores?";
BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps, skins and scores?\nThis will create a second copy of all files on disk.";
Icon = FontAwesome.Solid.Plane;

View File

@ -5,6 +5,7 @@ namespace osu.Game.Skinning
{
public enum GlobalSkinConfiguration
{
AnimationFramerate
AnimationFramerate,
LayeredHitSounds,
}
}

View File

@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Legacy;
namespace osu.Game.Skinning
{
@ -28,7 +29,17 @@ namespace osu.Game.Skinning
public Texture GetTexture(string componentName) => Source.GetTexture(componentName);
public virtual SampleChannel GetSample(ISampleInfo sampleInfo) => Source.GetSample(sampleInfo);
public virtual SampleChannel GetSample(ISampleInfo sampleInfo)
{
if (!(sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacySample))
return Source.GetSample(sampleInfo);
var playLayeredHitSounds = GetConfig<GlobalSkinConfiguration, bool>(GlobalSkinConfiguration.LayeredHitSounds);
if (legacySample.IsLayered && playLayeredHitSounds?.Value == false)
return new SampleChannelVirtual();
return Source.GetSample(sampleInfo);
}
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
}

View File

@ -1,11 +1,14 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Storyboards.Drawables
{
@ -17,7 +20,8 @@ namespace osu.Game.Storyboards.Drawables
private const double allowable_late_start = 100;
private readonly StoryboardSampleInfo sampleInfo;
private SampleChannel channel;
protected SampleChannel Channel { get; private set; }
public override bool RemoveWhenNotAlive => false;
@ -28,12 +32,16 @@ namespace osu.Game.Storyboards.Drawables
}
[BackgroundDependencyLoader]
private void load(IBindable<WorkingBeatmap> beatmap)
private void load(IBindable<WorkingBeatmap> beatmap, IBindable<IReadOnlyList<Mod>> mods)
{
channel = beatmap.Value.Skin.GetSample(sampleInfo);
Channel = beatmap.Value.Skin.GetSample(sampleInfo);
if (Channel == null)
return;
if (channel != null)
channel.Volume.Value = sampleInfo.Volume / 100.0;
Channel.Volume.Value = sampleInfo.Volume / 100.0;
foreach (var mod in mods.Value.OfType<IApplicableToSample>())
mod.ApplyToSample(Channel);
}
protected override void Update()
@ -44,7 +52,7 @@ namespace osu.Game.Storyboards.Drawables
if (Time.Current < sampleInfo.StartTime)
{
// We've rewound before the start time of the sample
channel?.Stop();
Channel?.Stop();
// In the case that the user fast-forwards to a point far beyond the start time of the sample,
// we want to be able to fall into the if-conditional below (therefore we must not have a life time end)
@ -56,7 +64,7 @@ namespace osu.Game.Storyboards.Drawables
// We've passed the start time of the sample. We only play the sample if we're within an allowable range
// from the sample's start, to reduce layering if we've been fast-forwarded far into the future
if (Time.Current - sampleInfo.StartTime < allowable_late_start)
channel?.Play();
Channel?.Play();
// In the case that the user rewinds to a point far behind the start time of the sample,
// we want to be able to fall into the if-conditional above (therefore we must not have a life time start)
@ -67,7 +75,7 @@ namespace osu.Game.Storyboards.Drawables
protected override void Dispose(bool isDisposing)
{
channel?.Stop();
Channel?.Stop();
base.Dispose(isDisposing);
}
}

View File

@ -24,6 +24,10 @@ namespace osu.Game.Tests.Beatmaps
public abstract class HitObjectSampleTest : PlayerTestScene
{
protected abstract IResourceStore<byte[]> Resources { get; }
protected LegacySkin Skin { get; private set; }
[Resolved]
private RulesetStore rulesetStore { get; set; }
private readonly SkinInfo userSkinInfo = new SkinInfo();
@ -64,6 +68,9 @@ namespace osu.Game.Tests.Beatmaps
{
using (var reader = new LineBufferedReader(Resources.GetStream($"Resources/SampleLookups/{filename}")))
currentTestBeatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
// populate ruleset for beatmap converters that require it to be present.
currentTestBeatmap.BeatmapInfo.Ruleset = rulesetStore.GetRuleset(currentTestBeatmap.BeatmapInfo.RulesetID);
});
});
}
@ -91,7 +98,7 @@ namespace osu.Game.Tests.Beatmaps
};
// Need to refresh the cached skin source to refresh the skin resource store.
dependencies.SkinSource = new SkinProvidingContainer(new LegacySkin(userSkinInfo, userSkinResourceStore, Audio));
dependencies.SkinSource = new SkinProvidingContainer(Skin = new LegacySkin(userSkinInfo, userSkinResourceStore, Audio));
});
}
@ -101,6 +108,9 @@ namespace osu.Game.Tests.Beatmaps
protected void AssertUserLookup(string name) => AddAssert($"\"{name}\" looked up from user skin",
() => !beatmapSkinResourceStore.PerformedLookups.Contains(name) && userSkinResourceStore.PerformedLookups.Contains(name));
protected void AssertNoLookup(string name) => AddAssert($"\"{name}\" not looked up",
() => !beatmapSkinResourceStore.PerformedLookups.Contains(name) && !userSkinResourceStore.PerformedLookups.Contains(name));
private class SkinSourceDependencyContainer : IReadOnlyDependencyContainer
{
public ISkinSource SkinSource;

View File

@ -24,8 +24,8 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2020.619.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.602.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.623.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.622.1" />
<PackageReference Include="Sentry" Version="2.1.3" />
<PackageReference Include="SharpCompress" Version="0.25.1" />
<PackageReference Include="NUnit" Version="3.12.0" />

View File

@ -70,8 +70,8 @@
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.619.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.602.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.623.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.622.1" />
</ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
<ItemGroup Label="Transitive Dependencies">
@ -80,7 +80,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2020.619.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.623.0" />
<PackageReference Include="SharpCompress" Version="0.25.1" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />