Merge branch 'master' into layered-hit-sounds

This commit is contained in:
Dan Balasescu
2020-06-26 18:38:32 +09:00
committed by GitHub
32 changed files with 512 additions and 185 deletions

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,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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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(@"Intro/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,10 @@ 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;
@ -66,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;
@ -77,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)
{
@ -84,12 +99,22 @@ 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;
ReadOnlySpan<float> temporalAmplitudes = (track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty).FrequencyAmplitudes.Span;
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++)
{
@ -128,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

@ -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

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