diff --git a/osu-framework b/osu-framework index 13af08782c..dd9faf4609 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 13af08782cfb4de054b2fb11a2cf8311ff895949 +Subproject commit dd9faf46095a761558abc6cb11ecd0e7498dba4e diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs index 69b56ef17d..a3ae103f28 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs @@ -44,11 +44,14 @@ namespace osu.Desktop.VisualTests.Tests time += 500; } - Add(new Player() + Add(new Player { - Beatmap = new Beatmap + Beatmap = new WorkingBeatmap { - HitObjects = objects + Beatmap = new Beatmap + { + HitObjects = objects + } } }); } diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index d48019c446..2d854ab3b7 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [TestFixture] public class OsuLegacyDecoderTest { - [TestFixtureSetUp] + [OneTimeSetUpAttribute] public void SetUp() { OsuLegacyDecoder.Register(); diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index caa56c26c2..568c08cfc6 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -10,7 +10,7 @@ namespace osu.Game.Tests.Beatmaps.IO [TestFixture] public class OszArchiveReaderTest { - [TestFixtureSetUp] + [OneTimeSetUpAttribute] public void SetUp() { OszArchiveReader.Register(); diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 04c09b3a2c..af6fa91661 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -33,7 +33,7 @@ - $(SolutionDir)\packages\NUnit.2.6.4\lib\nunit.framework.dll + $(SolutionDir)\packages\NUnit.3.5.0\lib\nunit.framework.dll $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1339\lib\net45\OpenTK.dll diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index c2b6b5932f..4769e2fb64 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -1,7 +1,6 @@ //Copyright (c) 2007-2016 ppy Pty Ltd . //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using OpenTK.Graphics; using osu.Game.Beatmaps.Objects; @@ -13,6 +12,7 @@ namespace osu.Game.Beatmaps public class Beatmap { public BeatmapInfo BeatmapInfo { get; set; } + public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; public List HitObjects { get; set; } public List ControlPoints { get; set; } public List ComboColors { get; set; } diff --git a/osu.Game/Beatmaps/Drawable/BeatmapGroup.cs b/osu.Game/Beatmaps/Drawable/BeatmapGroup.cs index 57ca1190e3..ebc2ab447c 100644 --- a/osu.Game/Beatmaps/Drawable/BeatmapGroup.cs +++ b/osu.Game/Beatmaps/Drawable/BeatmapGroup.cs @@ -2,6 +2,7 @@ //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.Linq; using osu.Framework; using osu.Framework.Graphics; @@ -27,6 +28,8 @@ namespace osu.Game.Beatmaps.Drawable private BeatmapGroupState state; + public List BeatmapPanels; + public BeatmapGroupState State { get { return state; } @@ -40,9 +43,6 @@ namespace osu.Game.Beatmaps.Drawable difficulties.Show(); header.State = PanelSelectedState.Selected; - - if (SelectedPanel == null) - ((BeatmapPanel)difficulties.Children.FirstOrDefault()).State = PanelSelectedState.Selected; break; case BeatmapGroupState.Collapsed: FadeTo(0.5f, 250); @@ -62,6 +62,16 @@ namespace osu.Game.Beatmaps.Drawable AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; + BeatmapPanels = beatmapSet.Beatmaps.Select(b => + new BeatmapPanel(b) + { + GainedSelection = panelGainedSelection, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.X, + }).ToList(); + + Children = new[] { new FlowContainer @@ -87,15 +97,7 @@ namespace osu.Game.Beatmaps.Drawable Spacing = new Vector2(0, 5), Direction = FlowDirection.VerticalOnly, Alpha = 0, - Children = this.beatmapSet.Beatmaps.Select(b => - new BeatmapPanel(this.beatmapSet, b) - { - GainedSelection = panelGainedSelection, - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.X, - } - ) + Children = BeatmapPanels } } } @@ -112,17 +114,28 @@ namespace osu.Game.Beatmaps.Drawable { State = BeatmapGroupState.Expanded; - SelectionChanged?.Invoke(this, SelectedPanel.Beatmap); + //we want to make sure one of our children is selected in the case none have been selected yet. + if (SelectedPanel == null) + BeatmapPanels.First().State = PanelSelectedState.Selected; + else + SelectionChanged?.Invoke(this, SelectedPanel.Beatmap); } private void panelGainedSelection(BeatmapPanel panel) { - State = BeatmapGroupState.Expanded; - - if (SelectedPanel != null) SelectedPanel.State = PanelSelectedState.NotSelected; - SelectedPanel = panel; + try + { + if (SelectedPanel == panel) return; - SelectionChanged?.Invoke(this, panel.Beatmap); + if (SelectedPanel != null) + SelectedPanel.State = PanelSelectedState.NotSelected; + SelectedPanel = panel; + } + finally + { + State = BeatmapGroupState.Expanded; + SelectionChanged?.Invoke(this, panel.Beatmap); + } } } diff --git a/osu.Game/Beatmaps/Drawable/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawable/BeatmapPanel.cs index 62b5f63335..cd28e909a8 100644 --- a/osu.Game/Beatmaps/Drawable/BeatmapPanel.cs +++ b/osu.Game/Beatmaps/Drawable/BeatmapPanel.cs @@ -26,7 +26,7 @@ namespace osu.Game.Beatmaps.Drawable GainedSelection?.Invoke(this); } - public BeatmapPanel(BeatmapSetInfo set, BeatmapInfo beatmap) + public BeatmapPanel(BeatmapInfo beatmap) { Beatmap = beatmap; Height *= 0.75f; @@ -67,7 +67,7 @@ namespace osu.Game.Beatmaps.Drawable }, new SpriteText { - Text = $" mapped by {(beatmap.Metadata ?? set.Metadata).Author}", + Text = $" mapped by {(beatmap.Metadata ?? beatmap.BeatmapSet.Metadata).Author}", TextSize = 16, }, } diff --git a/osu.Game/Beatmaps/Drawable/Panel.cs b/osu.Game/Beatmaps/Drawable/Panel.cs index 430c23836d..b0c0821197 100644 --- a/osu.Game/Beatmaps/Drawable/Panel.cs +++ b/osu.Game/Beatmaps/Drawable/Panel.cs @@ -37,7 +37,6 @@ namespace osu.Game.Beatmaps.Drawable set { if (state == value) return; - state = value; switch (state) diff --git a/osu.Game/Beatmaps/Objects/Catch/Drawable/DrawableFruit.cs b/osu.Game/Beatmaps/Objects/Catch/Drawable/DrawableFruit.cs index 028afff5cc..3a3883ab3b 100644 --- a/osu.Game/Beatmaps/Objects/Catch/Drawable/DrawableFruit.cs +++ b/osu.Game/Beatmaps/Objects/Catch/Drawable/DrawableFruit.cs @@ -34,8 +34,8 @@ namespace osu.Game.Beatmaps.Objects.Catch.Drawable Texture = game.Textures.Get(@"Menu/logo"); - Transforms.Add(new TransformPosition(Clock) { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = new Vector2(h.Position, -0.1f), EndValue = new Vector2(h.Position, 0.9f) }); - Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 }); + Transforms.Add(new TransformPosition { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = new Vector2(h.Position, -0.1f), EndValue = new Vector2(h.Position, 0.9f) }); + Transforms.Add(new TransformAlpha { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 }); Expire(true); } } diff --git a/osu.Game/Beatmaps/Objects/Mania/Drawable/DrawableNote.cs b/osu.Game/Beatmaps/Objects/Mania/Drawable/DrawableNote.cs index 132fb6ce36..939dc645cd 100644 --- a/osu.Game/Beatmaps/Objects/Mania/Drawable/DrawableNote.cs +++ b/osu.Game/Beatmaps/Objects/Mania/Drawable/DrawableNote.cs @@ -25,8 +25,8 @@ namespace osu.Game.Beatmaps.Objects.Mania.Drawable base.Load(game); Texture = game.Textures.Get(@"Menu/logo"); - Transforms.Add(new TransformPositionY(Clock) { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f }); - Transforms.Add(new TransformAlpha(Clock) { StartTime = note.StartTime + note.Duration + 200, EndTime = note.StartTime + note.Duration + 400, StartValue = 1, EndValue = 0 }); + Transforms.Add(new TransformPositionY() { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f }); + Transforms.Add(new TransformAlpha() { StartTime = note.StartTime + note.Duration + 200, EndTime = note.StartTime + note.Duration + 400, StartValue = 1, EndValue = 0 }); Expire(true); } } diff --git a/osu.Game/Beatmaps/Objects/Osu/Drawable/DrawableCircle.cs b/osu.Game/Beatmaps/Objects/Osu/Drawable/DrawableCircle.cs index bbfcac9d39..be2b7495f6 100644 --- a/osu.Game/Beatmaps/Objects/Osu/Drawable/DrawableCircle.cs +++ b/osu.Game/Beatmaps/Objects/Osu/Drawable/DrawableCircle.cs @@ -77,36 +77,36 @@ namespace osu.Game.Beatmaps.Objects.Osu.Drawable Flush(); //move to DrawableHitObject - Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime - 1000, EndTime = h.StartTime - 800, StartValue = 0, EndValue = 1 }); + Transforms.Add(new TransformAlpha { StartTime = h.StartTime - 1000, EndTime = h.StartTime - 800, StartValue = 0, EndValue = 1 }); - approachCircle.Transforms.Add(new TransformScale(Clock) { StartTime = h.StartTime - 1000, EndTime = h.StartTime, StartValue = new Vector2(2f), EndValue = new Vector2(0.6f) }); - approachCircle.Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime, EndTime = h.StartTime, StartValue = 1, EndValue = 0 }); + approachCircle.Transforms.Add(new TransformScale { StartTime = h.StartTime - 1000, EndTime = h.StartTime, StartValue = new Vector2(2f), EndValue = new Vector2(0.6f) }); + approachCircle.Transforms.Add(new TransformAlpha { StartTime = h.StartTime, EndTime = h.StartTime, StartValue = 1, EndValue = 0 }); - glow.Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime, EndTime = h.StartTime + 400, StartValue = glow.Alpha, EndValue = 0 }); + glow.Transforms.Add(new TransformAlpha { StartTime = h.StartTime, EndTime = h.StartTime + 400, StartValue = glow.Alpha, EndValue = 0 }); switch (state) { case ArmedState.Disarmed: - Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 }); + Transforms.Add(new TransformAlpha { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 }); break; case ArmedState.Armed: const float flashIn = 30; const float fadeOut = 800; - //Transforms.Add(new TransformScale(Clock) { StartTime = h.StartTime, EndTime = h.StartTime + 400, StartValue = Scale, EndValue = Scale * 1.1f }); + //Transforms.Add(new TransformScale() { StartTime = h.StartTime, EndTime = h.StartTime + 400, StartValue = Scale, EndValue = Scale * 1.1f }); - ring.Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0 }); - circle.Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0 }); - number.Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0 }); + ring.Transforms.Add(new TransformAlpha { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0 }); + circle.Transforms.Add(new TransformAlpha { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0 }); + number.Transforms.Add(new TransformAlpha { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0 }); - flash.Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0.8f }); - flash.Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn + 100, StartValue = 0.8f, EndValue = 0 }); + flash.Transforms.Add(new TransformAlpha { StartTime = h.StartTime, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 0.8f }); + flash.Transforms.Add(new TransformAlpha { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn + 100, StartValue = 0.8f, EndValue = 0 }); - explode.Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 1 }); + explode.Transforms.Add(new TransformAlpha { StartTime = h.StartTime, EndTime = h.StartTime + flashIn, StartValue = 0, EndValue = 1 }); - Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn + fadeOut, StartValue = 1, EndValue = 0 }); + Transforms.Add(new TransformAlpha { StartTime = h.StartTime + flashIn, EndTime = h.StartTime + flashIn + fadeOut, StartValue = 1, EndValue = 0 }); - Transforms.Add(new TransformScale(Clock) { StartTime = h.StartTime + h.Duration, EndTime = h.StartTime + h.Duration + 400, StartValue = Scale, EndValue = Scale * 1.5f, Easing = EasingTypes.OutQuad }); + Transforms.Add(new TransformScale { StartTime = h.StartTime + h.Duration, EndTime = h.StartTime + h.Duration + 400, StartValue = Scale, EndValue = Scale * 1.5f, Easing = EasingTypes.OutQuad }); break; } } diff --git a/osu.Game/Beatmaps/Objects/Taiko/Drawable/DrawableTaikoHit.cs b/osu.Game/Beatmaps/Objects/Taiko/Drawable/DrawableTaikoHit.cs index dafbe33415..c0354124c7 100644 --- a/osu.Game/Beatmaps/Objects/Taiko/Drawable/DrawableTaikoHit.cs +++ b/osu.Game/Beatmaps/Objects/Taiko/Drawable/DrawableTaikoHit.cs @@ -29,8 +29,8 @@ namespace osu.Game.Beatmaps.Objects.Taiko.Drawable Texture = game.Textures.Get(@"Menu/logo"); - Transforms.Add(new TransformPositionX(Clock) { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = 1.1f, EndValue = 0.1f }); - Transforms.Add(new TransformAlpha(Clock) { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 }); + Transforms.Add(new TransformPositionX { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = 1.1f, EndValue = 0.1f }); + Transforms.Add(new TransformAlpha { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 }); Expire(true); } } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs new file mode 100644 index 0000000000..4deed53465 --- /dev/null +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -0,0 +1,87 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using osu.Framework.Audio.Track; +using osu.Game.Beatmaps.Formats; +using osu.Game.Beatmaps.IO; +using osu.Game.Database; + +namespace osu.Game.Beatmaps +{ + public class WorkingBeatmap : IDisposable + { + public BeatmapInfo BeatmapInfo; + + public readonly ArchiveReader Reader; + + private Beatmap beatmap; + public Beatmap Beatmap + { + get + { + if (beatmap != null) return beatmap; + + try + { + using (var stream = new StreamReader(Reader.ReadFile(BeatmapInfo.Path))) + beatmap = BeatmapDecoder.GetDecoder(stream)?.Decode(stream); + } + catch { } + + return beatmap; + } + set { beatmap = value; } + } + + private AudioTrack track; + public AudioTrack Track + { + get + { + if (track != null) return track; + + try + { + var trackData = Reader.ReadFile(BeatmapInfo.Metadata.AudioFile); + if (trackData != null) + track = new AudioTrackBass(trackData); + } + catch { } + + return track; + } + set { track = value; } + } + + public WorkingBeatmap(BeatmapInfo beatmapInfo = null, ArchiveReader reader = null) + { + this.BeatmapInfo = beatmapInfo; + Reader = reader; + } + + private bool isDisposed; + + protected virtual void Dispose(bool disposing) + { + if (!isDisposed) + { + track?.Dispose(); + Reader?.Dispose(); + isDisposed = true; + } + } + + public void Dispose() + { + Dispose(true); + } + + public void TransferTo(WorkingBeatmap working) + { + if (track != null && working.BeatmapInfo.Metadata.AudioFile == BeatmapInfo.Metadata.AudioFile && working.BeatmapInfo.BeatmapSet.Path == BeatmapInfo.BeatmapSet.Path) + working.track = track; + } + } +} diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index 1b55b0eeaf..5d0834bad7 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -130,43 +130,56 @@ namespace osu.Game.Database { return ArchiveReader.GetReader(storage, beatmapSet.Path); } - + public BeatmapSetInfo GetBeatmapSet(int id) { return Query().Where(s => s.BeatmapSetID == id).FirstOrDefault(); } - + + public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null) + { + var beatmapSetInfo = Query().FirstOrDefault(s => s.BeatmapSetID == beatmapInfo.BeatmapSetID); + + //we need metadata + GetChildren(beatmapSetInfo); + + if (beatmapSetInfo == null) + throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetID} is not in the local database."); + + var reader = GetReader(beatmapSetInfo); + + if (beatmapInfo.Metadata == null) + beatmapInfo.Metadata = beatmapSetInfo.Metadata; + + var working = new WorkingBeatmap(beatmapInfo, reader); + + previous?.TransferTo(working); + + return working; + } + public Beatmap GetBeatmap(BeatmapInfo beatmapInfo) { - var beatmapSet = Query() - .Where(s => s.BeatmapSetID == beatmapInfo.BeatmapSetID).FirstOrDefault(); - if (beatmapSet == null) - throw new InvalidOperationException( - $@"Beatmap set {beatmapInfo.BeatmapSetID} is not in the local database."); - using (var reader = GetReader(beatmapSet)) - using (var stream = new StreamReader(reader.ReadFile(beatmapInfo.Path))) - { - var decoder = BeatmapDecoder.GetDecoder(stream); - return decoder.Decode(stream); - } + using (WorkingBeatmap data = GetWorkingBeatmap(beatmapInfo)) + return data.Beatmap; } - + public TableQuery Query() where T : class { return connection.Table(); } - + public T GetWithChildren(object id) where T : class { return connection.GetWithChildren(id); } - + public List GetAllWithChildren(Expression> filter = null, bool recursive = true) where T : class { return connection.GetAllWithChildren(filter, recursive); } - + public T GetChildren(T item, bool recursive = true) { if (item == null) return default(T); diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Database/BeatmapInfo.cs index 5b1d0c323c..4d18976ef0 100644 --- a/osu.Game/Database/BeatmapInfo.cs +++ b/osu.Game/Database/BeatmapInfo.cs @@ -7,7 +7,7 @@ using SQLiteNetExtensions.Attributes; namespace osu.Game.Database { - public class BeatmapInfo + public class BeatmapInfo : IEquatable { [PrimaryKey] public int BeatmapID { get; set; } @@ -64,5 +64,10 @@ namespace osu.Game.Database // Metadata public string Version { get; set; } + + public bool Equals(BeatmapInfo other) + { + return BeatmapID == other?.BeatmapID; + } } } \ No newline at end of file diff --git a/osu.Game/GameModes/Menu/Button.cs b/osu.Game/GameModes/Menu/Button.cs index b278cefbd9..5599f25ad3 100644 --- a/osu.Game/GameModes/Menu/Button.cs +++ b/osu.Game/GameModes/Menu/Button.cs @@ -117,7 +117,7 @@ namespace osu.Game.GameModes.Menu icon.RotateTo(10, offset, EasingTypes.InOutSine); icon.ScaleTo(new Vector2(1, 0.9f), offset, EasingTypes.Out); - icon.Transforms.Add(new TransformRotation(Clock) + icon.Transforms.Add(new TransformRotation { StartValue = -10, EndValue = 10, @@ -128,7 +128,7 @@ namespace osu.Game.GameModes.Menu LoopDelay = duration * 2 }); - icon.Transforms.Add(new TransformPosition(Clock) + icon.Transforms.Add(new TransformPosition { StartValue = Vector2.Zero, EndValue = new Vector2(0, -10), @@ -139,7 +139,7 @@ namespace osu.Game.GameModes.Menu LoopDelay = duration }); - icon.Transforms.Add(new TransformScale(Clock) + icon.Transforms.Add(new TransformScale { StartValue = new Vector2(1, 0.9f), EndValue = Vector2.One, @@ -150,7 +150,7 @@ namespace osu.Game.GameModes.Menu LoopDelay = duration }); - icon.Transforms.Add(new TransformPosition(Clock) + icon.Transforms.Add(new TransformPosition { StartValue = new Vector2(0, -10), EndValue = Vector2.Zero, @@ -161,7 +161,7 @@ namespace osu.Game.GameModes.Menu LoopDelay = duration }); - icon.Transforms.Add(new TransformScale(Clock) + icon.Transforms.Add(new TransformScale { StartValue = Vector2.One, EndValue = new Vector2(1, 0.9f), @@ -172,7 +172,7 @@ namespace osu.Game.GameModes.Menu LoopDelay = duration }); - icon.Transforms.Add(new TransformRotation(Clock) + icon.Transforms.Add(new TransformRotation { StartValue = 10, EndValue = -10, diff --git a/osu.Game/GameModes/Menu/Intro.cs b/osu.Game/GameModes/Menu/Intro.cs index f0bba5bddb..2973f83b6b 100644 --- a/osu.Game/GameModes/Menu/Intro.cs +++ b/osu.Game/GameModes/Menu/Intro.cs @@ -10,6 +10,8 @@ using osu.Framework.Graphics.Transformations; using osu.Game.GameModes.Backgrounds; using OpenTK.Graphics; using osu.Framework; +using osu.Framework.Configuration; +using osu.Game.Beatmaps; namespace osu.Game.GameModes.Menu { diff --git a/osu.Game/GameModes/OsuGameMode.cs b/osu.Game/GameModes/OsuGameMode.cs index b0928d8214..d8b73f5f07 100644 --- a/osu.Game/GameModes/OsuGameMode.cs +++ b/osu.Game/GameModes/OsuGameMode.cs @@ -3,17 +3,20 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; +using osu.Framework.Configuration; using osu.Framework.GameModes; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; using osu.Game.Graphics.Background; using osu.Game.Graphics.Containers; namespace osu.Game.GameModes { - public class OsuGameMode : GameMode + public abstract class OsuGameMode : GameMode { internal BackgroundMode Background { get; private set; } @@ -23,6 +26,64 @@ namespace osu.Game.GameModes /// protected virtual BackgroundMode CreateBackground() => null; + private bool boundToBeatmap; + private Bindable beatmap; + + public WorkingBeatmap Beatmap + { + get + { + bindBeatmap(); + return beatmap.Value; + } + set + { + bindBeatmap(); + beatmap.Value = value; + } + } + + private void bindBeatmap() + { + if (beatmap == null) + beatmap = new Bindable(); + + if (!boundToBeatmap) + { + beatmap.ValueChanged += beatmap_ValueChanged; + boundToBeatmap = true; + } + } + + protected override void Dispose(bool isDisposing) + { + if (boundToBeatmap) + beatmap.ValueChanged -= beatmap_ValueChanged; + + base.Dispose(isDisposing); + } + + private void beatmap_ValueChanged(object sender, EventArgs e) + { + OnBeatmapChanged(beatmap.Value); + } + + public override bool Push(GameMode mode) + { + OsuGameMode nextOsu = mode as OsuGameMode; + if (nextOsu != null) + { + nextOsu.beatmap = beatmap; + } + + return base.Push(mode); + } + + protected virtual void OnBeatmapChanged(WorkingBeatmap beatmap) + { + + } + protected override void OnEntering(GameMode last) { OsuGameMode lastOsu = last as OsuGameMode; diff --git a/osu.Game/GameModes/Play/ComboCounter.cs b/osu.Game/GameModes/Play/ComboCounter.cs index c40d7a4964..ebc89c102e 100644 --- a/osu.Game/GameModes/Play/ComboCounter.cs +++ b/osu.Game/GameModes/Play/ComboCounter.cs @@ -174,7 +174,7 @@ namespace osu.Game.GameModes.Play protected virtual void OnCountRolling(ulong currentValue, ulong newValue) { - transformRoll(new TransformComboRoll(Clock), currentValue, newValue); + transformRoll(new TransformComboRoll(), currentValue, newValue); } protected virtual void OnCountIncrement(ulong currentValue, ulong newValue) { @@ -248,11 +248,11 @@ namespace osu.Game.GameModes.Play protected class TransformComboRoll : Transform { - public override ulong CurrentValue + protected override ulong CurrentValue { get { - double time = Time; + double time = CurrentTime ?? 0; if (time < StartTime) return StartValue; if (time >= EndTime) return EndValue; @@ -265,11 +265,6 @@ namespace osu.Game.GameModes.Play base.Apply(d); (d as ComboCounter).DisplayedCount = CurrentValue; } - - public TransformComboRoll(IClock clock) - : base(clock) - { - } } } } diff --git a/osu.Game/GameModes/Play/ComboResultCounter.cs b/osu.Game/GameModes/Play/ComboResultCounter.cs index a07e35b263..fd96906d18 100644 --- a/osu.Game/GameModes/Play/ComboResultCounter.cs +++ b/osu.Game/GameModes/Play/ComboResultCounter.cs @@ -41,11 +41,11 @@ namespace osu.Game.GameModes.Play protected class TransformComboResult : Transform { - public override ulong CurrentValue + protected override ulong CurrentValue { get { - double time = Time; + double time = CurrentTime ?? 0; if (time < StartTime) return StartValue; if (time >= EndTime) return EndValue; @@ -58,11 +58,6 @@ namespace osu.Game.GameModes.Play base.Apply(d); (d as ComboResultCounter).DisplayedCount = CurrentValue; } - - public TransformComboResult(IClock clock) - : base(clock) - { - } } } } diff --git a/osu.Game/GameModes/Play/PlaySongSelect.cs b/osu.Game/GameModes/Play/PlaySongSelect.cs index b4d675fc0b..309cd3119b 100644 --- a/osu.Game/GameModes/Play/PlaySongSelect.cs +++ b/osu.Game/GameModes/Play/PlaySongSelect.cs @@ -16,8 +16,11 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics.UserInterface; using System.Threading.Tasks; +using osu.Framework.Audio.Track; using osu.Game.Beatmaps.Drawable; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Beatmaps; +using osu.Framework.GameModes; namespace osu.Game.GameModes.Play { @@ -26,10 +29,12 @@ namespace osu.Game.GameModes.Play private Bindable playMode; private BeatmapDatabase database; private BeatmapGroup selectedBeatmapGroup; + private BeatmapInfo selectedBeatmapInfo; // TODO: use currently selected track as bg protected override BackgroundMode CreateBackground() => new BackgroundModeCustom(@"Backgrounds/bg4"); private ScrollContainer scrollContainer; - private FlowContainer setList; + private FlowContainer beatmapSetFlow; + private TrackManager trackManager; /// Optionally provide a database to use instead of the OsuGame one. public PlaySongSelect(BeatmapDatabase database = null) @@ -73,7 +78,7 @@ namespace osu.Game.GameModes.Play Origin = Anchor.CentreRight, Children = new Drawable[] { - setList = new FlowContainer + beatmapSetFlow = new FlowContainer { Padding = new MarginPadding { Left = 25, Top = 25, Bottom = 25 + bottomToolHeight }, RelativeSizeAxes = Axes.X, @@ -107,7 +112,7 @@ namespace osu.Game.GameModes.Play Colour = new Color4(238, 51, 153, 255), Action = () => Push(new Player { BeatmapInfo = selectedBeatmapGroup.SelectedPanel.Beatmap, - PlayMode = playMode.Value + PreferredPlayMode = playMode.Value }), }, } @@ -123,7 +128,7 @@ namespace osu.Game.GameModes.Play if (osuGame != null) { playMode = osuGame.PlayMode; - playMode.ValueChanged += PlayMode_ValueChanged; + playMode.ValueChanged += playMode_ValueChanged; // Temporary: scrollContainer.Padding = new MarginPadding { Top = osuGame.Toolbar.Height }; } @@ -133,22 +138,76 @@ namespace osu.Game.GameModes.Play database.BeatmapSetAdded += s => Schedule(() => addBeatmapSet(s)); + trackManager = game.Audio.Track; + Task.Factory.StartNew(addBeatmapSets); } + protected override void OnEntering(GameMode last) + { + base.OnEntering(last); + ensurePlayingSelected(); + } + + protected override void OnResuming(GameMode last) + { + ensurePlayingSelected(); + base.OnResuming(last); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); if (playMode != null) - playMode.ValueChanged -= PlayMode_ValueChanged; + playMode.ValueChanged -= playMode_ValueChanged; } - private void PlayMode_ValueChanged(object sender, EventArgs e) + private void playMode_ValueChanged(object sender, EventArgs e) { } - private void selectBeatmap(BeatmapGroup group, BeatmapInfo beatmap) + /// + /// The global Beatmap was changed. + /// + protected override void OnBeatmapChanged(WorkingBeatmap beatmap) { + base.OnBeatmapChanged(beatmap); + selectBeatmap(beatmap.BeatmapInfo); + } + + private void selectBeatmap(BeatmapInfo beatmap) + { + if (beatmap.Equals(selectedBeatmapInfo)) + return; + + //this is VERY temporary logic. + beatmapSetFlow.Children.Cast().Any(b => + { + var panel = b.BeatmapPanels.FirstOrDefault(p => p.Beatmap.Equals(beatmap)); + if (panel != null) + { + panel.State = PanelSelectedState.Selected; + return true; + } + + return false; + }); + } + + /// + /// selection has been changed as the result of interaction with the carousel. + /// + private void selectionChanged(BeatmapGroup group, BeatmapInfo beatmap) + { + selectedBeatmapInfo = beatmap; + + if (!beatmap.Equals(Beatmap?.BeatmapInfo)) + { + Beatmap = database.GetWorkingBeatmap(beatmap, Beatmap); + } + + ensurePlayingSelected(); + if (selectedBeatmapGroup == group) return; @@ -158,6 +217,17 @@ namespace osu.Game.GameModes.Play selectedBeatmapGroup = group; } + private void ensurePlayingSelected() + { + var track = Beatmap?.Track; + + if (track != null) + { + trackManager.SetExclusive(track); + track.Start(); + } + } + private void addBeatmapSet(BeatmapSetInfo beatmapSet) { beatmapSet = database.GetWithChildren(beatmapSet.BeatmapSetID); @@ -165,10 +235,22 @@ namespace osu.Game.GameModes.Play beatmapSet.Beatmaps = beatmapSet.Beatmaps.OrderBy(b => b.BaseDifficulty.OverallDifficulty).ToList(); Schedule(() => { - var group = new BeatmapGroup(beatmapSet) { SelectionChanged = selectBeatmap }; - setList.Add(group); - if (setList.Children.Count() == 1) - group.State = BeatmapGroupState.Expanded; + var group = new BeatmapGroup(beatmapSet) { SelectionChanged = selectionChanged }; + beatmapSetFlow.Add(group); + if (Beatmap == null) + { + if (beatmapSetFlow.Children.Count() == 1) + group.State = BeatmapGroupState.Expanded; + } + else + { + if (selectedBeatmapInfo?.Equals(Beatmap.BeatmapInfo) != true) + { + var panel = group.BeatmapPanels.FirstOrDefault(p => p.Beatmap.Equals(Beatmap.BeatmapInfo)); + if (panel != null) + panel.State = PanelSelectedState.Selected; + } + } }); } diff --git a/osu.Game/GameModes/Play/Player.cs b/osu.Game/GameModes/Play/Player.cs index 4e18f06f5b..5f3e2954a5 100644 --- a/osu.Game/GameModes/Play/Player.cs +++ b/osu.Game/GameModes/Play/Player.cs @@ -1,7 +1,6 @@ //Copyright (c) 2007-2016 ppy Pty Ltd . //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Objects; @@ -12,6 +11,9 @@ using osu.Game.GameModes.Play.Osu; using osu.Game.GameModes.Play.Taiko; using osu.Framework; using osu.Game.Database; +using osu.Framework.Timing; +using osu.Framework.GameModes; +using osu.Framework.Audio.Track; namespace osu.Game.GameModes.Play { @@ -20,9 +22,13 @@ namespace osu.Game.GameModes.Play protected override BackgroundMode CreateBackground() => new BackgroundModeCustom(@"Backgrounds/bg4"); public BeatmapInfo BeatmapInfo; - public Beatmap Beatmap; - public PlayMode PlayMode; + public PlayMode PreferredPlayMode; + + protected override IFrameBasedClock Clock => playerClock; + + private InterpolatingFramedClock playerClock; + private IAdjustableClock sourceClock; public override void Load(BaseGame game) { @@ -31,7 +37,7 @@ namespace osu.Game.GameModes.Play try { if (Beatmap == null) - Beatmap = ((OsuGame)game).Beatmaps.GetBeatmap(BeatmapInfo); + Beatmap = ((OsuGame)game).Beatmaps.GetWorkingBeatmap(BeatmapInfo); } catch { @@ -40,17 +46,45 @@ namespace osu.Game.GameModes.Play return; } + AudioTrack track = Beatmap.Track; + + if (track != null) + { + game.Audio.Track.SetExclusive(track); + sourceClock = track; + } + + sourceClock = (IAdjustableClock)track ?? new StopwatchClock(); + playerClock = new InterpolatingFramedClock(sourceClock); + + Schedule(() => + { + sourceClock.Reset(); + sourceClock.Start(); + }); + HitRenderer hitRenderer; ScoreOverlay scoreOverlay; - switch (PlayMode) + var beatmap = Beatmap.Beatmap; + + if (beatmap.BeatmapInfo?.Mode > PlayMode.Osu) + { + //we only support osu! mode for now because the hitobject parsing is crappy and needs a refactor. + Exit(); + return; + } + + PlayMode usablePlayMode = beatmap.BeatmapInfo?.Mode > PlayMode.Osu ? beatmap.BeatmapInfo.Mode : PreferredPlayMode; + + switch (usablePlayMode) { default: scoreOverlay = new ScoreOverlayOsu(); hitRenderer = new OsuHitRenderer { - Objects = Beatmap.HitObjects, + Objects = beatmap.HitObjects, Anchor = Anchor.Centre, Origin = Anchor.Centre }; @@ -60,7 +94,7 @@ namespace osu.Game.GameModes.Play hitRenderer = new TaikoHitRenderer { - Objects = Beatmap.HitObjects, + Objects = beatmap.HitObjects, Anchor = Anchor.Centre, Origin = Anchor.Centre }; @@ -70,7 +104,7 @@ namespace osu.Game.GameModes.Play hitRenderer = new CatchHitRenderer { - Objects = Beatmap.HitObjects, + Objects = beatmap.HitObjects, Anchor = Anchor.Centre, Origin = Anchor.Centre }; @@ -80,7 +114,7 @@ namespace osu.Game.GameModes.Play hitRenderer = new ManiaHitRenderer { - Objects = Beatmap.HitObjects, + Objects = beatmap.HitObjects, Anchor = Anchor.Centre, Origin = Anchor.Centre }; @@ -96,5 +130,11 @@ namespace osu.Game.GameModes.Play scoreOverlay, }; } + + protected override void Update() + { + base.Update(); + playerClock.ProcessFrame(); + } } } \ No newline at end of file diff --git a/osu.Game/GameModes/Play/Results.cs b/osu.Game/GameModes/Ranking/Results.cs similarity index 80% rename from osu.Game/GameModes/Play/Results.cs rename to osu.Game/GameModes/Ranking/Results.cs index 4974966aa9..2ffb1660c1 100644 --- a/osu.Game/GameModes/Play/Results.cs +++ b/osu.Game/GameModes/Ranking/Results.cs @@ -1,16 +1,11 @@ //Copyright (c) 2007-2016 ppy Pty Ltd . //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using osu.Framework.GameModes; using osu.Game.GameModes.Backgrounds; using OpenTK.Graphics; -namespace osu.Game.GameModes.Play +namespace osu.Game.GameModes.Ranking { class Results : GameModeWhiteBox { diff --git a/osu.Game/Graphics/UserInterface/PercentageCounter.cs b/osu.Game/Graphics/UserInterface/PercentageCounter.cs index b12126deb6..b045089dcd 100644 --- a/osu.Game/Graphics/UserInterface/PercentageCounter.cs +++ b/osu.Game/Graphics/UserInterface/PercentageCounter.cs @@ -57,11 +57,6 @@ namespace osu.Game.Graphics.UserInterface base.Apply(d); (d as PercentageCounter).DisplayedCount = CurrentValue; } - - public TransformAccuracy(IClock clock) - : base(clock) - { - } } } } diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index 69e7b6e495..33a1677bd9 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -194,14 +194,12 @@ namespace osu.Game.Graphics.UserInterface /// protected virtual void TransformCount(T currentValue, T newValue) { - object[] parameters = { Clock }; - Debug.Assert( TransformType.IsSubclassOf(typeof(Transform)) || TransformType == typeof(Transform), @"transformType should be a subclass of Transform." ); - TransformCount((Transform)Activator.CreateInstance(TransformType, parameters), currentValue, newValue); + TransformCount((Transform)Activator.CreateInstance(TransformType), currentValue, newValue); } /// diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index eb857a3207..48f4da1fcf 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -56,11 +56,11 @@ namespace osu.Game.Graphics.UserInterface protected class TransformScore : Transform { - public override ulong CurrentValue + protected override ulong CurrentValue { get { - double time = Time; + double time = CurrentTime ?? 0; if (time < StartTime) return StartValue; if (time >= EndTime) return EndValue; @@ -73,11 +73,6 @@ namespace osu.Game.Graphics.UserInterface base.Apply(d); (d as ScoreCounter).DisplayedCount = CurrentValue; } - - public TransformScore(IClock clock) - : base(clock) - { - } } } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 549e79b800..2515ace38e 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -70,7 +70,10 @@ namespace osu.Game RelativeSizeAxes = Axes.Both, ActivateRequested = delegate { volume.Show(); } }, - intro = new Intro(), + intro = new Intro + { + Beatmap = Beatmap + }, Toolbar = new Toolbar { OnHome = delegate { MainMenu?.MakeCurrent(); }, diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 4df5683493..bfa07d72c8 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -1,10 +1,13 @@ -using osu.Framework; +using System; +using osu.Framework; +using osu.Framework.Configuration; using osu.Framework.GameModes; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.IO; using osu.Game.Configuration; using osu.Game.Database; @@ -32,6 +35,8 @@ namespace osu.Game public CursorContainer Cursor; + public readonly Bindable Beatmap = new Bindable(); + public OsuGameBase() { AddInternal(ratioContainer = new RatioAdjust()); @@ -41,6 +46,13 @@ namespace osu.Game Options = new Options(), Cursor = new OsuCursorContainer() }; + + Beatmap.ValueChanged += Beatmap_ValueChanged; + } + + private void Beatmap_ValueChanged(object sender, EventArgs e) + { + throw new NotImplementedException(); } public override void Load(BaseGame game) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f044ef9a6e..b73bc8ffda 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -64,6 +64,7 @@ + @@ -124,7 +125,7 @@ - +