1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-28 06:03:08 +08:00

Merge branch 'master' into selection-update

This commit is contained in:
Dean Herbert 2019-06-10 17:51:40 +09:00 committed by GitHub
commit d7cb932606
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
119 changed files with 2727 additions and 708 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
custom: https://osu.ppy.sh/home/support

View File

@ -10,6 +10,8 @@ This project is still heavily under development, but is in a state where users a
We are accepting bug reports (please report with as much detail as possible). Feature requests are welcome as long as you read and understand the contribution guidelines listed below.
Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh/home/changelog).
## Requirements
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
@ -20,17 +22,24 @@ We are accepting bug reports (please report with as much detail as possible). Fe
### Releases
If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled.
![](https://puu.sh/DCmvA/f6a74f5fbb.png)
- Windows (x64) users should download and run `install.exe`.
- macOS users (10.12 "Sierra" and higher) should download and run `osu.app.zip`.
- iOS users can join the [TestFlight beta program](https://t.co/xQJmHkfC18).
If you are not interested in developing the game, you can consume our [binary releases](https://github.com/ppy/osu/releases).
**Latest build:***
| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) |
| ------------- | ------------- |
- **Linux** users are recommended to self-compile until we have official deployment in place.
- **iOS** users can join the [TestFlight beta program](https://t.co/xQJmHkfC18) (note that due to high demand this is regularly full).
- **Android** users can self-compile, and expect a public beta soon.
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
### Downloading the source code
Clone the repository **including submodules**:
Clone the repository:
```shell
git clone https://github.com/ppy/osu
@ -45,7 +54,7 @@ git pull
### Building
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided below.
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided [below](#contributing).
> Visual Studio Code users must run the `Restore` task before any build attempt.

View File

@ -20,14 +20,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
Ruleset = ruleset.RulesetInfo
Ruleset = ruleset
}
};

View File

@ -29,14 +29,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo
Ruleset = ruleset
}
};

View File

@ -16,14 +16,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo
Ruleset = ruleset
}
};

View File

@ -23,13 +23,13 @@ namespace osu.Game.Rulesets.Catch.Tests
AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);
}
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo =
{
Ruleset = ruleset.RulesetInfo,
Ruleset = ruleset,
BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f }
}
};

View File

@ -17,14 +17,14 @@ namespace osu.Game.Rulesets.Osu.Tests
{
}
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo
Ruleset = ruleset
}
};

View File

@ -20,7 +20,6 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual;
using osuTK;
@ -299,7 +298,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
AddStep("load player", () =>
{
Beatmap.Value = new TestWorkingBeatmap(new Beatmap<OsuHitObject>
Beatmap.Value = CreateWorkingBeatmap(new Beatmap<OsuHitObject>
{
HitObjects =
{
@ -323,7 +322,7 @@ namespace osu.Game.Rulesets.Osu.Tests
BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 },
Ruleset = new OsuRuleset().RulesetInfo
},
}, Clock);
});
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });

View File

@ -2,13 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Primitives;
using osuTK;
using osuTK.Graphics;
using osuTK.Graphics.ES30;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@ -19,8 +16,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private readonly SliderPath path;
protected Path Path => path;
private readonly BufferedContainer container;
public float PathRadius
{
get => path.PathRadius;
@ -44,8 +39,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
path.AccentColour = value;
container.ForceRedraw();
}
}
@ -61,8 +54,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
path.BorderColour = value;
container.ForceRedraw();
}
}
@ -78,23 +69,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
path.BorderSize = value;
container.ForceRedraw();
}
}
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
protected SliderBody()
{
InternalChild = container = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
Child = path = new SliderPath { Blending = BlendingMode.None }
};
container.Attach(RenderbufferInternalFormat.DepthComponent16);
InternalChild = path = new SliderPath();
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
@ -103,11 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
/// Sets the vertices of the path which should be drawn by this <see cref="SliderBody"/>.
/// </summary>
/// <param name="vertices">The vertices</param>
protected void SetVertices(IReadOnlyList<Vector2> vertices)
{
path.Vertices = vertices;
container.ForceRedraw();
}
protected void SetVertices(IReadOnlyList<Vector2> vertices) => path.Vertices = vertices;
private class SliderPath : SmoothPath
{

View File

@ -54,7 +54,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
for (int i = 0; i < max_sprites; i++)
{
parts[i].InvalidationID = 0;
// InvalidationID 1 forces an update of each part of the cursor trail the first time ApplyState is run on the draw node
// This is to prevent garbage data from being sent to the vertex shader, resulting in visual issues on some platforms
parts[i].InvalidationID = 1;
parts[i].WasUpdated = true;
}
}
@ -210,7 +212,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
Vector2 pos = parts[i].Position;
float localTime = parts[i].Time;
texture.DrawQuad(
DrawQuad(
texture,
new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
DrawColourInfo.Colour,
null,

View File

@ -18,7 +18,6 @@ using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual;
using osuTK;
using osu.Game.Rulesets.Scoring;
@ -64,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
var controlPointInfo = new ControlPointInfo();
controlPointInfo.TimingPoints.Add(new TimingControlPoint());
WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
{
HitObjects = new List<HitObject> { new CentreHit() },
BeatmapInfo = new BeatmapInfo
@ -79,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Ruleset = new TaikoRuleset().RulesetInfo
},
ControlPointInfo = controlPointInfo
}, Clock);
});
Add(playfieldContainer = new Container
{

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
@ -54,7 +55,7 @@ namespace osu.Game.Tests.Visual.Background
private RulesetStore rulesets;
[BackgroundDependencyLoader]
private void load(GameHost host)
private void load(GameHost host, AudioManager audio)
{
factory = new DatabaseContextFactory(LocalStorage);
factory.ResetDatabase();
@ -68,7 +69,7 @@ namespace osu.Game.Tests.Visual.Background
usage.Migrate();
Dependencies.Cache(rulesets = new RulesetStore(factory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null, host, Beatmap.Default));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default));
Dependencies.Cache(new OsuConfigManager(LocalStorage));
manager.Import(TestResources.GetTestBeatmapForImport());

View File

@ -111,16 +111,19 @@ namespace osu.Game.Tests.Visual.Components
private class TestPreviewTrackManager : PreviewTrackManager
{
protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TestPreviewTrack(beatmapSetInfo, trackManager);
protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore);
protected class TestPreviewTrack : TrackManagerPreviewTrack
{
public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager)
private readonly ITrackStore trackManager;
public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager)
: base(beatmapSetInfo, trackManager)
{
this.trackManager = trackManager;
}
protected override Track GetTrack() => new TrackVirtual { Length = 100000 };
protected override Track GetTrack() => trackManager.GetVirtual(100000);
}
}
}

View File

@ -7,7 +7,6 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual.Editor
{
@ -19,7 +18,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, Clock);
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
Child = new ComposeScreen();
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -30,9 +31,9 @@ namespace osu.Game.Tests.Visual.Editor
};
[BackgroundDependencyLoader]
private void load()
private void load(AudioManager audio)
{
Beatmap.Value = new WaveformTestBeatmap();
Beatmap.Value = new WaveformTestBeatmap(audio);
Children = new Drawable[]
{

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
using osuTK;
using osuTK.Graphics;
@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.Editor
}
};
Beatmap.Value = new TestWorkingBeatmap(testBeatmap, Clock);
Beatmap.Value = CreateWorkingBeatmap(testBeatmap);
Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock };
}

View File

@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
@ -21,7 +20,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null);
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
Add(new SummaryTimeline
{

View File

@ -18,7 +18,6 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
@ -45,7 +44,7 @@ namespace osu.Game.Tests.Visual.Editor
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = new TestWorkingBeatmap(new Beatmap
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
{
HitObjects = new List<HitObject>
{

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Screens.Edit.Components;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Editor
Size = new Vector2(200, 100)
};
Beatmap.Value = new TestWorkingBeatmap(new Beatmap(), Clock);
Beatmap.Value = CreateWorkingBeatmap(new Beatmap());
Child = playback;
}

View File

@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
@ -10,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Osu;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
@ -20,9 +22,9 @@ namespace osu.Game.Tests.Visual.Editor
private WorkingBeatmap waveformBeatmap;
[BackgroundDependencyLoader]
private void load()
private void load(AudioManager audio)
{
waveformBeatmap = new WaveformTestBeatmap();
waveformBeatmap = new WaveformTestBeatmap(audio);
}
[TestCase(1f)]
@ -91,7 +93,7 @@ namespace osu.Game.Tests.Visual.Editor
Child = graph = new TestWaveformGraph
{
RelativeSizeAxes = Axes.Both,
Waveform = new DummyWorkingBeatmap().Waveform,
Waveform = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).Waveform,
},
};
});

View File

@ -0,0 +1,50 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneFailAnimation : AllPlayersTestScene
{
protected override Player CreatePlayer(Ruleset ruleset)
{
Mods.Value = Array.Empty<Mod>();
return new FailPlayer();
}
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(AllPlayersTestScene),
typeof(TestPlayer),
typeof(Player),
};
protected override void AddCheckSteps()
{
AddUntilStep("wait for fail", () => Player.HasFailed);
AddUntilStep("wait for fail overlay", () => ((FailPlayer)Player).FailOverlay.State == Visibility.Visible);
}
private class FailPlayer : TestPlayer
{
public new FailOverlay FailOverlay => base.FailOverlay;
public FailPlayer()
: base(false, false)
{
}
protected override void LoadComplete()
{
base.LoadComplete();
ScoreProcessor.FailConditions += _ => true;
}
}
}
}

View File

@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void TestPauseAfterFail()
{
AddUntilStep("wait for fail", () => Player.HasFailed);
AddAssert("fail overlay shown", () => Player.FailOverlayVisible);
AddUntilStep("fail overlay shown", () => Player.FailOverlayVisible);
confirmClockRunning(false);

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
@ -16,7 +16,6 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual.Gameplay
{
@ -29,7 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void Setup() => Schedule(() =>
{
InputManager.Child = stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
Beatmap.Value = new TestWorkingBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), Clock);
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
});
[Test]

View File

@ -3,7 +3,6 @@
using System;
using osu.Framework.Lists;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Screens.Play;
@ -43,9 +42,9 @@ namespace osu.Game.Tests.Visual.Gameplay
});
}
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock clock)
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
{
var working = base.CreateWorkingBeatmap(beatmap, clock);
var working = base.CreateWorkingBeatmap(beatmap);
workingWeakReferences.Add(working);
return working;
}

View File

@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
@ -57,7 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("set name", () => Room.Name.Value = "Room name");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo }));
AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo }));
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
AddStep("clear name", () => Room.Name.Value = "");

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets.Osu;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.Online
@ -25,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online
[BackgroundDependencyLoader]
private void load()
{
var beatmap = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null);
var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true;
beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true;

View File

@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.Online
FlagName = @"TH",
},
},
Rank = ScoreRank.F,
Rank = ScoreRank.D,
PP = 160,
MaxCombo = 1234,
TotalScore = 123456,

View File

@ -0,0 +1,55 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Overlays.Profile.Sections;
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneShowMoreButton : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ShowMoreButton),
};
public TestSceneShowMoreButton()
{
ShowMoreButton button = null;
int fireCount = 0;
Add(button = new ShowMoreButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = () =>
{
fireCount++;
// ReSharper disable once AccessToModifiedClosure
// ReSharper disable once PossibleNullReferenceException
Scheduler.AddDelayed(() => button.IsLoading = false, 2000);
}
});
AddStep("click button", () => button.Click());
AddAssert("action fired once", () => fireCount == 1);
AddAssert("is in loading state", () => button.IsLoading);
AddStep("click button", () => button.Click());
AddAssert("action not fired", () => fireCount == 1);
AddAssert("is in loading state", () => button.IsLoading);
AddUntilStep("wait for loaded", () => !button.IsLoading);
AddStep("click button", () => button.Click());
AddAssert("action fired twice", () => fireCount == 2);
AddAssert("is in loading state", () => button.IsLoading);
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select;
@ -18,7 +19,8 @@ namespace osu.Game.Tests.Visual.SongSelect
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BeatmapDetails) };
public TestSceneBeatmapDetailArea()
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
BeatmapDetailArea detailsArea;
Add(detailsArea = new BeatmapDetailArea
@ -28,7 +30,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Size = new Vector2(550f, 450f),
});
AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap
AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
BeatmapInfo =
{
@ -56,7 +58,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}
);
AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap
AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
BeatmapInfo =
{
@ -82,7 +84,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}
});
AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap
AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
BeatmapInfo =
{
@ -107,7 +109,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}
});
AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap
AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
BeatmapInfo =
{
@ -133,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}
});
AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap
AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
{
BeatmapInfo =
{

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
@ -18,7 +18,6 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps;
using osuTK;
namespace osu.Game.Tests.Visual.SongSelect
@ -136,7 +135,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () =>
{
infoBefore = infoWedge.Info;
infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : new TestWorkingBeatmap(b);
infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b);
});
AddUntilStep("wait for async load", () => infoWedge.Info != infoBefore);

View File

@ -188,7 +188,7 @@ namespace osu.Game.Tests.Visual.SongSelect
},
new ScoreInfo
{
Rank = ScoreRank.F,
Rank = ScoreRank.D,
Accuracy = 0.6025,
MaxCombo = 244,
TotalScore = 1707827,
@ -206,7 +206,7 @@ namespace osu.Game.Tests.Visual.SongSelect
},
new ScoreInfo
{
Rank = ScoreRank.F,
Rank = ScoreRank.D,
Accuracy = 0.5140,
MaxCombo = 244,
TotalScore = 1707827,
@ -224,7 +224,7 @@ namespace osu.Game.Tests.Visual.SongSelect
},
new ScoreInfo
{
Rank = ScoreRank.F,
Rank = ScoreRank.D,
Accuracy = 0.4222,
MaxCombo = 244,
TotalScore = 1707827,

View File

@ -8,6 +8,7 @@ using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.MathUtils;
@ -79,7 +80,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}
[BackgroundDependencyLoader]
private void load(GameHost host)
private void load(GameHost host, AudioManager audio)
{
factory = new DatabaseContextFactory(LocalStorage);
factory.ResetDatabase();
@ -93,7 +94,7 @@ namespace osu.Game.Tests.Visual.SongSelect
usage.Migrate();
Dependencies.Cache(rulesets = new RulesetStore(factory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, null, host, defaultBeatmap = Beatmap.Default));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default));
Beatmap.SetDefault();
}

View File

@ -0,0 +1,50 @@
// 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.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Tests.Visual.UserInterface
{
/// <summary>
/// An abstract test case which exposes small cells arranged in a grid.
/// Useful for displaying multiple configurations of a tested component at a glance.
/// </summary>
public abstract class OsuGridTestScene : OsuTestScene
{
private readonly Drawable[,] cells;
/// <summary>
/// The amount of rows in the grid.
/// </summary>
protected readonly int Rows;
/// <summary>
/// The amount of columns in the grid.
/// </summary>
protected readonly int Cols;
/// <summary>
/// Constructs a grid test case with the given dimensions.
/// </summary>
protected OsuGridTestScene(int rows, int cols)
{
Rows = rows;
Cols = cols;
GridContainer testContainer;
Add(testContainer = new GridContainer { RelativeSizeAxes = Axes.Both });
cells = new Drawable[rows, cols];
for (int r = 0; r < rows; r++)
for (int c = 0; c < cols; c++)
cells[r, c] = new Container { RelativeSizeAxes = Axes.Both };
testContainer.Content = cells.ToJagged();
}
protected Container Cell(int index) => (Container)cells[index / Cols, index % Cols];
protected Container Cell(int row, int col) => (Container)cells[row, col];
}
}

View File

@ -84,7 +84,6 @@ namespace osu.Game.Tests.Visual.UserInterface
testLocalCursor();
testUserCursorOverride();
testMultipleLocalCursors();
ReturnUserInput();
}
/// <summary>

View File

@ -3,13 +3,12 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneLoadingAnimation : GridTestScene //todo: this should be an OsuTestScene
public class TestSceneLoadingAnimation : OsuGridTestScene
{
public TestSceneLoadingAnimation()
: base(2, 2)

View File

@ -0,0 +1,72 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOsuAnimatedButton : OsuGridTestScene
{
public TestSceneOsuAnimatedButton()
: base(3, 2)
{
Cell(0).Add(new BaseContainer("relative sized")
{
RelativeSizeAxes = Axes.Both,
});
Cell(1).Add(new BaseContainer("auto sized")
{
AutoSizeAxes = Axes.Both
});
Cell(2).Add(new BaseContainer("relative Y auto X")
{
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X
});
Cell(3).Add(new BaseContainer("relative X auto Y")
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
});
Cell(4).Add(new BaseContainer("fixed")
{
Size = new Vector2(100),
});
Cell(5).Add(new BaseContainer("fixed")
{
Size = new Vector2(100, 50),
});
AddToggleStep("toggle enabled", toggle =>
{
for (int i = 0; i < 6; i++)
((BaseContainer)Cell(i).Child).Action = toggle ? () => { } : (Action)null;
});
}
public class BaseContainer : OsuAnimatedButton
{
public BaseContainer(string text)
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Add(new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = text
});
}
}
}
}

View File

@ -0,0 +1,194 @@
// 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.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneOsuHoverContainer : ManualInputManagerTestScene
{
private OsuHoverTestContainer hoverContainer;
private Box colourContainer;
[SetUp]
public void SetUp() => Schedule(() =>
{
Child = hoverContainer = new OsuHoverTestContainer
{
Enabled = { Value = true },
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(100),
Child = colourContainer = new Box
{
RelativeSizeAxes = Axes.Both,
},
};
doMoveOut();
});
[Description("Checks IsHovered property value on a container when it is hovered/unhovered.")]
[TestCase(true, TestName = "Enabled_Check_IsHovered")]
[TestCase(false, TestName = "Disabled_Check_IsHovered")]
public void TestIsHoveredHasProperValue(bool isEnabled)
{
setContainerEnabledTo(isEnabled);
checkNotHovered();
moveToText();
checkHovered();
moveOut();
checkNotHovered();
moveToText();
checkHovered();
moveOut();
checkNotHovered();
}
[Test]
[Description("Checks colour fading on an enabled container when it is hovered/unhovered.")]
public void TestTransitionWhileEnabled()
{
enableContainer();
checkColour(OsuHoverTestContainer.IDLE_COLOUR);
moveToText();
waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
moveOut();
waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR);
moveToText();
waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
moveOut();
waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR);
}
[Test]
[Description("Checks colour fading on a disabled container when it is hovered/unhovered.")]
public void TestNoTransitionWhileDisabled()
{
disableContainer();
checkColour(OsuHoverTestContainer.IDLE_COLOUR);
moveToText();
checkColour(OsuHoverTestContainer.IDLE_COLOUR);
moveOut();
checkColour(OsuHoverTestContainer.IDLE_COLOUR);
moveToText();
checkColour(OsuHoverTestContainer.IDLE_COLOUR);
moveOut();
checkColour(OsuHoverTestContainer.IDLE_COLOUR);
}
[Test]
[Description("Checks that when a disabled & hovered container gets enabled, colour fading happens")]
public void TestBecomesEnabledTransition()
{
disableContainer();
checkColour(OsuHoverTestContainer.IDLE_COLOUR);
moveToText();
checkColour(OsuHoverTestContainer.IDLE_COLOUR);
enableContainer();
waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
}
[Test]
[Description("Checks that when an enabled & hovered container gets disabled, colour fading happens")]
public void TestBecomesDisabledTransition()
{
enableContainer();
checkColour(OsuHoverTestContainer.IDLE_COLOUR);
moveToText();
waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
disableContainer();
waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR);
}
[Test]
[Description("Checks that when a hovered container gets enabled and disabled multiple times, colour fading happens")]
public void TestDisabledChangesMultipleTimes()
{
enableContainer();
checkColour(OsuHoverTestContainer.IDLE_COLOUR);
moveToText();
waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
disableContainer();
waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR);
enableContainer();
waitUntilColourIs(OsuHoverTestContainer.HOVER_COLOUR);
disableContainer();
waitUntilColourIs(OsuHoverTestContainer.IDLE_COLOUR);
}
private void enableContainer() => setContainerEnabledTo(true);
private void disableContainer() => setContainerEnabledTo(false);
private void setContainerEnabledTo(bool newValue)
{
string word = newValue ? "Enable" : "Disable";
AddStep($"{word} container", () => hoverContainer.Enabled.Value = newValue);
}
private void moveToText() => AddStep("Move mouse to text", () => InputManager.MoveMouseTo(hoverContainer));
private void moveOut() => AddStep("Move out", doMoveOut);
private void checkHovered() => AddAssert("Check hovered", () => hoverContainer.IsHovered);
private void checkNotHovered() => AddAssert("Check not hovered", () => !hoverContainer.IsHovered);
private void checkColour(ColourInfo expectedColour)
=> AddAssert($"Check colour to be '{expectedColour}'", () => currentColour.Equals(expectedColour));
private void waitUntilColourIs(ColourInfo expectedColour)
=> AddUntilStep($"Wait until hover colour is {expectedColour}", () => currentColour.Equals(expectedColour));
private ColourInfo currentColour => colourContainer.DrawColourInfo.Colour;
/// <summary>
/// Moves the cursor to top left corner of the screen
/// </summary>
private void doMoveOut()
=> InputManager.MoveMouseTo(new Vector2(InputManager.ScreenSpaceDrawQuad.TopLeft.X, InputManager.ScreenSpaceDrawQuad.TopLeft.Y));
private sealed class OsuHoverTestContainer : OsuHoverContainer
{
public static readonly Color4 HOVER_COLOUR = Color4.Red;
public static readonly Color4 IDLE_COLOUR = Color4.Green;
public OsuHoverTestContainer()
{
HoverColour = HOVER_COLOUR;
IdleColour = IDLE_COLOUR;
}
}
}
}

View File

@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.UserInterface
TestUpdateableBeatmapBackgroundSprite background = null;
AddStep("load null beatmap", () => Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both });
AddUntilStep("wait for load", () => background.ContentLoaded);
AddUntilStep("content loaded", () => background.ContentLoaded);
}
[Test]

View File

@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
@ -19,12 +20,14 @@ namespace osu.Game.Tests
{
private readonly ZipArchiveReader reader;
private readonly Stream stream;
private readonly ITrackStore trackStore;
public WaveformTestBeatmap()
: base(new BeatmapInfo())
public WaveformTestBeatmap(AudioManager audioManager)
: base(new BeatmapInfo(), audioManager)
{
stream = TestResources.GetTestBeatmapStream();
reader = new ZipArchiveReader(stream);
trackStore = audioManager.GetTrackStore(reader);
}
public override void Dispose()
@ -32,17 +35,19 @@ namespace osu.Game.Tests
base.Dispose();
stream?.Dispose();
reader?.Dispose();
trackStore?.Dispose();
}
protected override IBeatmap GetBeatmap() => createTestBeatmap();
protected override Texture GetBackground() => null;
protected override Waveform GetWaveform() => new Waveform(getAudioStream());
protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile));
protected override Track GetTrack() => new TrackBass(getAudioStream());
protected override Track GetTrack() => trackStore.Get(firstAudioFile);
private string firstAudioFile => reader.Filenames.First(f => f.EndsWith(".mp3"));
private Stream getAudioStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".mp3")));
private Stream getBeatmapStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu")));
private Beatmap createTestBeatmap()

View File

@ -1,6 +1,10 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
@ -12,27 +16,28 @@ using osu.Game.Beatmaps;
namespace osu.Game.Audio
{
/// <summary>
/// A central store for the retrieval of <see cref="PreviewTrack"/>s.
/// </summary>
public class PreviewTrackManager : Component
{
private readonly BindableDouble muteBindable = new BindableDouble();
private AudioManager audio;
private TrackManager trackManager;
private PreviewTrackStore trackStore;
private TrackManagerPreviewTrack current;
[BackgroundDependencyLoader]
private void load(AudioManager audio, FrameworkConfigManager config)
{
trackManager = new TrackManager(new OnlineStore());
// this is a temporary solution to get around muting ourselves.
// todo: update this once we have a BackgroundTrackManager or similar.
trackStore = new PreviewTrackStore(new OnlineStore());
audio.AddItem(trackStore);
trackStore.AddAdjustment(AdjustableProperty.Volume, audio.VolumeTrack);
this.audio = audio;
audio.AddItem(trackManager);
config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume);
config.BindWith(FrameworkSetting.VolumeMusic, trackStore.Volume);
}
/// <summary>
@ -42,19 +47,19 @@ namespace osu.Game.Audio
/// <returns>The playable <see cref="PreviewTrack"/>.</returns>
public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo)
{
var track = CreatePreviewTrack(beatmapSetInfo, trackManager);
var track = CreatePreviewTrack(beatmapSetInfo, trackStore);
track.Started += () =>
{
current?.Stop();
current = track;
audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable);
audio.Tracks.AddAdjustment(AdjustableProperty.Volume, muteBindable);
};
track.Stopped += () =>
{
current = null;
audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable);
audio.Tracks.RemoveAdjustment(AdjustableProperty.Volume, muteBindable);
};
return track;
@ -81,16 +86,16 @@ namespace osu.Game.Audio
/// <summary>
/// Creates the <see cref="TrackManagerPreviewTrack"/>.
/// </summary>
protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager) => new TrackManagerPreviewTrack(beatmapSetInfo, trackManager);
protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TrackManagerPreviewTrack(beatmapSetInfo, trackStore);
protected class TrackManagerPreviewTrack : PreviewTrack
{
public IPreviewTrackOwner Owner { get; private set; }
private readonly BeatmapSetInfo beatmapSetInfo;
private readonly TrackManager trackManager;
private readonly ITrackStore trackManager;
public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, TrackManager trackManager)
public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager)
{
this.beatmapSetInfo = beatmapSetInfo;
this.trackManager = trackManager;
@ -104,5 +109,46 @@ namespace osu.Game.Audio
protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3");
}
private class PreviewTrackStore : AudioCollectionManager<AdjustableAudioComponent>, ITrackStore
{
private readonly IResourceStore<byte[]> store;
internal PreviewTrackStore(IResourceStore<byte[]> store)
{
this.store = store;
}
public Track GetVirtual(double length = double.PositiveInfinity)
{
if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(PreviewTrackStore)}");
var track = new TrackVirtual(length);
AddItem(track);
return track;
}
public Track Get(string name)
{
if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(PreviewTrackStore)}");
if (string.IsNullOrEmpty(name)) return null;
var dataStream = store.GetStream(name);
if (dataStream == null)
return null;
Track track = new TrackBass(dataStream);
AddItem(track);
return track;
}
public Task<Track> GetAsync(string name) => Task.Run(() => Get(name));
public Stream GetStream(string name) => store.GetStream(name);
public IEnumerable<string> GetAvailableResources() => store.GetAvailableResources();
}
}
}

View File

@ -342,6 +342,7 @@ namespace osu.Game.Beatmaps
OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID,
Beatmaps = new List<BeatmapInfo>(),
Metadata = beatmap.Metadata,
DateAdded = DateTimeOffset.UtcNow
};
}
@ -429,7 +430,7 @@ namespace osu.Game.Beatmaps
private readonly IBeatmap beatmap;
public DummyConversionBeatmap(IBeatmap beatmap)
: base(beatmap.BeatmapInfo)
: base(beatmap.BeatmapInfo, null)
{
this.beatmap = beatmap;
}

View File

@ -20,14 +20,12 @@ namespace osu.Game.Beatmaps
protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap
{
private readonly IResourceStore<byte[]> store;
private readonly AudioManager audioManager;
public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, TextureStore textureStore, BeatmapInfo beatmapInfo, AudioManager audioManager)
: base(beatmapInfo)
: base(beatmapInfo, audioManager)
{
this.store = store;
this.textureStore = textureStore;
this.audioManager = audioManager;
}
protected override IBeatmap GetBeatmap()
@ -47,6 +45,8 @@ namespace osu.Game.Beatmaps
private TextureStore textureStore;
private ITrackStore trackStore;
protected override bool BackgroundStillValid(Texture b) => false; // bypass lazy logic. we want to return a new background each time for refcounting purposes.
protected override Texture GetBackground()
@ -68,8 +68,7 @@ namespace osu.Game.Beatmaps
{
try
{
var trackData = store.GetStream(getPathForFile(Metadata.AudioFile));
return trackData == null ? null : new TrackBass(trackData);
return (trackStore ?? (trackStore = AudioManager.GetTrackStore(store))).Get(getPathForFile(Metadata.AudioFile));
}
catch
{
@ -77,6 +76,14 @@ namespace osu.Game.Beatmaps
}
}
public override void RecycleTrack()
{
base.RecycleTrack();
trackStore?.Dispose();
trackStore = null;
}
public override void TransferTo(WorkingBeatmap other)
{
base.TransferTo(other);
@ -135,7 +142,7 @@ namespace osu.Game.Beatmaps
try
{
skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager);
skin = new LegacyBeatmapSkin(BeatmapInfo, store, AudioManager);
}
catch (Exception e)
{

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 System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
@ -20,6 +21,8 @@ namespace osu.Game.Beatmaps
set => onlineBeatmapSetID = value > 0 ? value : null;
}
public DateTimeOffset DateAdded { get; set; }
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
public BeatmapMetadata Metadata { get; set; }

View File

@ -1,11 +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 System;
using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
namespace osu.Game.Beatmaps
@ -16,32 +12,15 @@ namespace osu.Game.Beatmaps
/// </summary>
public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap>
{
private AudioManager audioManager;
private WorkingBeatmap lastBeatmap;
protected BindableBeatmap(WorkingBeatmap defaultValue)
: base(defaultValue)
{
BindValueChanged(b => updateAudioTrack(b.NewValue), true);
}
/// <summary>
/// Registers an <see cref="AudioManager"/> for <see cref="Track"/>s to be added to.
/// </summary>
/// <param name="audioManager">The <see cref="AudioManager"/> to register.</param>
protected void RegisterAudioManager([NotNull] AudioManager audioManager)
{
if (this.audioManager != null) throw new InvalidOperationException($"Cannot register multiple {nameof(AudioManager)}s.");
this.audioManager = audioManager;
ValueChanged += b => registerAudioTrack(b.NewValue);
// If the track has changed prior to this being called, let's register it
if (Value != Default)
registerAudioTrack(Value);
}
private void registerAudioTrack(WorkingBeatmap beatmap)
private void updateAudioTrack(WorkingBeatmap beatmap)
{
var trackLoaded = lastBeatmap?.TrackLoaded ?? false;
@ -55,18 +34,9 @@ namespace osu.Game.Beatmaps
lastBeatmap.RecycleTrack();
}
audioManager.Track.AddItem(beatmap.Track);
}
lastBeatmap = beatmap;
}
/// <summary>
/// Retrieve a new <see cref="BindableBeatmap"/> instance weakly bound to this <see cref="BindableBeatmap"/>.
/// If you are further binding to events of the retrieved <see cref="BindableBeatmap"/>, ensure a local reference is held.
/// </summary>
[NotNull]
public new abstract BindableBeatmap GetBoundCopy();
}
}

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 System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -31,24 +32,8 @@ namespace osu.Game.Beatmaps.Drawables
/// </summary>
protected virtual double UnloadDelay => 10000;
private BeatmapInfo lastModel;
private bool firstLoad = true;
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Drawable content, double timeBeforeLoad)
{
return new DelayedLoadUnloadWrapper(() =>
{
// If DelayedLoadUnloadWrapper is attempting to RELOAD the same content (Beatmap), that means that it was
// previously UNLOADED and thus its children have been disposed of, so we need to recreate them here.
if (!firstLoad && lastModel == Beatmap.Value)
return CreateDrawable(Beatmap.Value);
// If the model has changed since the previous unload (or if there was no load), then we can safely use the given content
lastModel = Beatmap.Value;
firstLoad = false;
return content;
}, timeBeforeLoad, UnloadDelay);
}
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad)
=> new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay);
protected override Drawable CreateDrawable(BeatmapInfo model)
{

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Textures;
@ -16,9 +17,9 @@ namespace osu.Game.Beatmaps
{
public class DummyWorkingBeatmap : WorkingBeatmap
{
private readonly OsuGameBase game;
private readonly TextureStore textures;
public DummyWorkingBeatmap(OsuGameBase game = null)
public DummyWorkingBeatmap(AudioManager audio, TextureStore textures)
: base(new BeatmapInfo
{
Metadata = new BeatmapMetadata
@ -34,16 +35,16 @@ namespace osu.Game.Beatmaps
OverallDifficulty = 0,
},
Ruleset = new DummyRulesetInfo()
})
}, audio)
{
this.game = game;
this.textures = textures;
}
protected override IBeatmap GetBeatmap() => new Beatmap();
protected override Texture GetBackground() => game?.Textures.Get(@"Backgrounds/bg4");
protected override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4");
protected override Track GetTrack() => new TrackVirtual { Length = 1000 };
protected override Track GetTrack() => GetVirtualTrack();
private class DummyRulesetInfo : RulesetInfo
{

View File

@ -11,15 +11,17 @@ using osu.Framework.IO.File;
using System.IO;
using System.Linq;
using System.Threading;
using osu.Framework.Audio;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
namespace osu.Game.Beatmaps
{
public abstract partial class WorkingBeatmap : IDisposable
public abstract class WorkingBeatmap : IDisposable
{
public readonly BeatmapInfo BeatmapInfo;
@ -27,8 +29,11 @@ namespace osu.Game.Beatmaps
public readonly BeatmapMetadata Metadata;
protected WorkingBeatmap(BeatmapInfo beatmapInfo)
protected AudioManager AudioManager { get; }
protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager)
{
AudioManager = audioManager;
BeatmapInfo = beatmapInfo;
BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
@ -46,13 +51,39 @@ namespace osu.Game.Beatmaps
return b;
});
track = new RecyclableLazy<Track>(() => GetTrack() ?? new VirtualBeatmapTrack(Beatmap));
track = new RecyclableLazy<Track>(() => GetTrack() ?? GetVirtualTrack());
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
waveform = new RecyclableLazy<Waveform>(GetWaveform);
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
skin = new RecyclableLazy<Skin>(GetSkin);
}
protected virtual Track GetVirtualTrack()
{
const double excess_length = 1000;
var lastObject = Beatmap.HitObjects.LastOrDefault();
double length;
switch (lastObject)
{
case null:
length = excess_length;
break;
case IHasEndTime endTime:
length = endTime.EndTime + excess_length;
break;
default:
length = lastObject.StartTime + excess_length;
break;
}
return AudioManager.Tracks.GetVirtual(length);
}
/// <summary>
/// Saves the <see cref="Beatmaps.Beatmap"/>.
/// </summary>
@ -150,6 +181,7 @@ namespace osu.Game.Beatmaps
public bool SkinLoaded => skin.IsResultAvailable;
public Skin Skin => skin.Value;
protected virtual Skin GetSkin() => new DefaultSkin();
private readonly RecyclableLazy<Skin> skin;
@ -175,7 +207,7 @@ namespace osu.Game.Beatmaps
/// Eagerly dispose of the audio track associated with this <see cref="WorkingBeatmap"/> (if any).
/// Accessing track again will load a fresh instance.
/// </summary>
public void RecycleTrack() => track.Recycle();
public virtual void RecycleTrack() => track.Recycle();
public class RecyclableLazy<T>
{

View File

@ -1,41 +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 osu.Framework.Audio.Track;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Beatmaps
{
public partial class WorkingBeatmap
{
/// <summary>
/// A type of <see cref="TrackVirtual"/> which provides a valid length based on the <see cref="HitObject"/>s of an <see cref="IBeatmap"/>.
/// </summary>
protected class VirtualBeatmapTrack : TrackVirtual
{
private const double excess_length = 1000;
public VirtualBeatmapTrack(IBeatmap beatmap)
{
var lastObject = beatmap.HitObjects.LastOrDefault();
switch (lastObject)
{
case null:
Length = excess_length;
break;
case IHasEndTime endTime:
Length = endTime.EndTime + excess_length;
break;
default:
Length = lastObject.StartTime + excess_length;
break;
}
}
}
}
}

View File

@ -15,6 +15,8 @@ namespace osu.Game.Configuration
public int? Variant { get; set; }
public int? SkinInfoID { get; set; }
[Column("Key")]
public string Key { get; set; }

View File

@ -214,7 +214,6 @@ namespace osu.Game.Graphics.Backgrounds
base.Draw(vertexAction);
shader.Bind();
texture.TextureGL.Bind();
Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy;
@ -231,7 +230,8 @@ namespace osu.Game.Graphics.Backgrounds
ColourInfo colourInfo = DrawColourInfo.Colour;
colourInfo.ApplyChild(particle.Colour);
texture.DrawTriangle(
DrawTriangle(
texture,
triangle,
colourInfo,
null,

View File

@ -31,7 +31,7 @@ namespace osu.Game.Graphics.Containers
{
if (AutoSizeAxes != Axes.None)
{
content.RelativeSizeAxes = RelativeSizeAxes;
content.RelativeSizeAxes = (Axes.Both & ~AutoSizeAxes);
content.AutoSizeAxes = AutoSizeAxes;
}

View File

@ -51,8 +51,8 @@ namespace osu.Game.Graphics.Containers
if (osuGame != null)
OverlayActivationMode.BindTo(osuGame.OverlayActivationMode);
samplePopIn = audio.Sample.Get(@"UI/overlay-pop-in");
samplePopOut = audio.Sample.Get(@"UI/overlay-pop-out");
samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in");
samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out");
StateChanged += onStateChanged;
}

View File

@ -24,7 +24,13 @@ namespace osu.Game.Graphics.Containers
{
Enabled.ValueChanged += e =>
{
if (!e.NewValue) unhover();
if (isHovered)
{
if (e.NewValue)
fadeIn();
else
fadeOut();
}
};
}
@ -32,27 +38,28 @@ namespace osu.Game.Graphics.Containers
protected override bool OnHover(HoverEvent e)
{
if (isHovered)
return false;
isHovered = true;
if (!Enabled.Value)
return false;
EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint));
isHovered = true;
fadeIn();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
unhover();
base.OnHoverLost(e);
}
private void unhover()
{
if (!isHovered) return;
if (!isHovered)
return;
isHovered = false;
EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint));
fadeOut();
base.OnHoverLost(e);
}
[BackgroundDependencyLoader]
@ -67,5 +74,9 @@ namespace osu.Game.Graphics.Containers
base.LoadComplete();
EffectTargets.ForEach(d => d.FadeColour(IdleColour));
}
private void fadeIn() => EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint));
private void fadeOut() => EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint));
}
}

View File

@ -51,7 +51,7 @@ namespace osu.Game.Graphics
screenshotFormat = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat);
captureMenuCursor = config.GetBindable<bool>(OsuSetting.ScreenshotCaptureMenuCursor);
shutter = audio.Sample.Get("UI/shutter");
shutter = audio.Samples.Get("UI/shutter");
}
public bool OnPressed(GlobalAction action)

View File

@ -31,7 +31,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}");
sampleClick = audio.Samples.Get($@"UI/generic-select{SampleSet.GetDescription()}");
}
}
}

View File

@ -37,7 +37,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}");
sampleHover = audio.Samples.Get($@"UI/generic-hover{SampleSet.GetDescription()}");
}
}

View File

@ -66,6 +66,7 @@ namespace osu.Game.Graphics.UserInterface
set
{
Content.RelativeSizeAxes = Axes.None;
Content.AutoSizeAxes = Axes.None;
Content.Size = value;
}
}

View File

@ -74,6 +74,12 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
if (AutoSizeAxes != Axes.None)
{
content.RelativeSizeAxes = (Axes.Both & ~AutoSizeAxes);
content.AutoSizeAxes = AutoSizeAxes;
}
Enabled.BindValueChanged(enabled => this.FadeColour(enabled.NewValue ? Color4.White : colours.Gray9, 200, Easing.OutQuint), true);
}

View File

@ -17,11 +17,11 @@ namespace osu.Game.Graphics.UserInterface
/// <summary>
/// A button with added default sound effects.
/// </summary>
public class OsuButton : Button
public abstract class OsuButton : Button
{
private Box hover;
public OsuButton()
protected OsuButton()
{
Height = 40;

View File

@ -112,8 +112,8 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleChecked = audio.Sample.Get(@"UI/check-on");
sampleUnchecked = audio.Sample.Get(@"UI/check-off");
sampleChecked = audio.Samples.Get(@"UI/check-on");
sampleUnchecked = audio.Samples.Get(@"UI/check-off");
}
}
}

View File

@ -71,8 +71,8 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleHover = audio.Sample.Get(@"UI/generic-hover");
sampleClick = audio.Sample.Get(@"UI/generic-select");
sampleHover = audio.Samples.Get(@"UI/generic-hover");
sampleClick = audio.Samples.Get(@"UI/generic-select");
BackgroundColour = Color4.Transparent;
BackgroundColourHover = OsuColour.FromHex(@"172023");

View File

@ -86,7 +86,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colours)
{
sample = audio.Sample.Get(@"UI/sliderbar-notch");
sample = audio.Samples.Get(@"UI/sliderbar-notch");
AccentColour = colours.Pink;
}

View File

@ -15,6 +15,8 @@ namespace osu.Game.IO.Archives
/// </summary>
public abstract Stream GetStream(string name);
public IEnumerable<string> GetAvailableResources() => Filenames;
public abstract void Dispose();
/// <summary>

View File

@ -0,0 +1,498 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20190525060824_SkinSettings")]
partial class SkinSettings
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Key")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int?>("ScoreInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("ScoreInfoID");
b.ToTable("ScoreFileInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("Accuracy")
.HasColumnType("DECIMAL(1,4)");
b.Property<int>("BeatmapInfoID");
b.Property<int>("Combo");
b.Property<DateTimeOffset>("Date");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int>("MaxCombo");
b.Property<string>("ModsJson")
.HasColumnName("Mods");
b.Property<long?>("OnlineScoreID");
b.Property<double?>("PP");
b.Property<int>("Rank");
b.Property<int>("RulesetID");
b.Property<string>("StatisticsJson")
.HasColumnName("Statistics");
b.Property<long>("TotalScore");
b.Property<long?>("UserID")
.HasColumnName("UserID");
b.Property<string>("UserString")
.HasColumnName("User");
b.HasKey("ID");
b.HasIndex("BeatmapInfoID");
b.HasIndex("OnlineScoreID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("ScoreInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Settings")
.HasForeignKey("SkinInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Scoring.ScoreInfo")
.WithMany("Files")
.HasForeignKey("ScoreInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,54 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class SkinSettings : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"create table Settings_dg_tmp
(
ID INTEGER not null
constraint PK_Settings
primary key autoincrement,
Key TEXT not null,
RulesetID INTEGER,
Value TEXT,
Variant INTEGER,
SkinInfoID int
constraint Settings_SkinInfo_ID_fk
references SkinInfo
on delete restrict
);
insert into Settings_dg_tmp(ID, Key, RulesetID, Value, Variant) select ID, Key, RulesetID, Value, Variant from Settings;
drop table Settings;
alter table Settings_dg_tmp rename to Settings;
create index IX_Settings_RulesetID_Variant
on Settings (RulesetID, Variant);
create index Settings_SkinInfoID_index
on Settings (SkinInfoID);
");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Settings_SkinInfo_SkinInfoID",
table: "Settings");
migrationBuilder.DropIndex(
name: "IX_Settings_SkinInfoID",
table: "Settings");
migrationBuilder.DropColumn(
name: "SkinInfoID",
table: "Settings");
}
}
}

View File

@ -0,0 +1,489 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20190605091246_AddDateAddedColumnToBeatmapSet")]
partial class AddDateAddedColumnToBeatmapSet
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Key")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int?>("ScoreInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("ScoreInfoID");
b.ToTable("ScoreFileInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("Accuracy")
.HasColumnType("DECIMAL(1,4)");
b.Property<int>("BeatmapInfoID");
b.Property<int>("Combo");
b.Property<DateTimeOffset>("Date");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int>("MaxCombo");
b.Property<string>("ModsJson")
.HasColumnName("Mods");
b.Property<long?>("OnlineScoreID");
b.Property<double?>("PP");
b.Property<int>("Rank");
b.Property<int>("RulesetID");
b.Property<string>("StatisticsJson")
.HasColumnName("Statistics");
b.Property<long>("TotalScore");
b.Property<long?>("UserID")
.HasColumnName("UserID");
b.Property<string>("UserString")
.HasColumnName("User");
b.HasKey("ID");
b.HasIndex("BeatmapInfoID");
b.HasIndex("OnlineScoreID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("ScoreInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Scoring.ScoreInfo")
.WithMany("Files")
.HasForeignKey("ScoreInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class AddDateAddedColumnToBeatmapSet : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTimeOffset>(
name: "DateAdded",
table: "BeatmapSetInfo",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DateAdded",
table: "BeatmapSetInfo");
}
}
}

View File

@ -166,6 +166,8 @@ namespace osu.Game.Migrations
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
@ -203,6 +205,8 @@ namespace osu.Game.Migrations
b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue")
.HasColumnName("Value");
@ -210,6 +214,8 @@ namespace osu.Game.Migrations
b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
@ -442,6 +448,13 @@ namespace osu.Game.Migrations
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Settings")
.HasForeignKey("SkinInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")

View File

@ -36,7 +36,7 @@ namespace osu.Game.Online.API.Requests.Responses
public string MessageHtml { get; set; }
[JsonProperty("major")]
public bool? Major { get; set; }
public bool Major { get; set; }
[JsonProperty("created_at")]
public DateTimeOffset? CreatedAt { get; set; }

View File

@ -81,6 +81,9 @@ namespace osu.Game.Online.Chat
if (user == null)
throw new ArgumentNullException(nameof(user));
if (user.Id == api.LocalUser.Value.Id)
return;
CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Users.Any(u => u.Id == user.Id))
?? new Channel(user);
}

View File

@ -161,7 +161,7 @@ namespace osu.Game
dependencies.CacheAs<IAPIProvider>(API);
var defaultBeatmap = new DummyWorkingBeatmap(this);
var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
@ -193,9 +193,9 @@ namespace osu.Game
// tracks play so loud our samples can't keep up.
// this adds a global reduction of track volume for the time being.
Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio);
beatmap = new OsuBindableBeatmap(defaultBeatmap);
dependencies.CacheAs<IBindable<WorkingBeatmap>>(beatmap);
dependencies.CacheAs(beatmap);
@ -281,23 +281,10 @@ namespace osu.Game
private class OsuBindableBeatmap : BindableBeatmap
{
public OsuBindableBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager)
: this(defaultValue)
{
RegisterAudioManager(audioManager);
}
public OsuBindableBeatmap(WorkingBeatmap defaultValue)
: base(defaultValue)
{
}
public override BindableBeatmap GetBoundCopy()
{
var copy = new OsuBindableBeatmap(Default);
copy.BindTo(this);
return copy;
}
}
private class OsuUserInputManager : UserInputManager

View File

@ -46,7 +46,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Text = "#1",
Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true)
},
rank = new DrawableRank(ScoreRank.F)
rank = new DrawableRank(ScoreRank.D)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -13,6 +13,7 @@ using System.Text.RegularExpressions;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using osuTK.Graphics;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.Changelog
{
@ -45,8 +46,12 @@ namespace osu.Game.Overlays.Changelog
Direction = FillDirection.Vertical,
},
};
}
foreach (var categoryEntries in build.ChangelogEntries.GroupBy(b => b.Category).OrderBy(c => c.Key))
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
foreach (var categoryEntries in Build.ChangelogEntries.GroupBy(b => b.Category).OrderBy(c => c.Key))
{
ChangelogEntries.Add(new OsuSpriteText
{
@ -69,34 +74,69 @@ namespace osu.Game.Overlays.Changelog
Margin = new MarginPadding { Vertical = 5 },
};
var entryColour = entry.Major ? colours.YellowLight : Color4.White;
title.AddIcon(FontAwesome.Solid.Check, t =>
{
t.Font = fontSmall;
t.Colour = entryColour;
t.Padding = new MarginPadding { Left = -17, Right = 5 };
});
title.AddText(entry.Title, t => { t.Font = fontLarge; });
title.AddText(entry.Title, t =>
{
t.Font = fontLarge;
t.Colour = entryColour;
});
if (!string.IsNullOrEmpty(entry.Repository))
{
title.AddText(" (", t => t.Font = fontLarge);
title.AddText(" (", t =>
{
t.Font = fontLarge;
t.Colour = entryColour;
});
title.AddLink($"{entry.Repository.Replace("ppy/", "")}#{entry.GithubPullRequestId}", entry.GithubUrl, Online.Chat.LinkAction.External,
creationParameters: t => { t.Font = fontLarge; });
title.AddText(")", t => t.Font = fontLarge);
creationParameters: t =>
{
t.Font = fontLarge;
t.Colour = entryColour;
});
title.AddText(")", t =>
{
t.Font = fontLarge;
t.Colour = entryColour;
});
}
title.AddText(" by ", t => t.Font = fontMedium);
title.AddText(" by ", t =>
{
t.Font = fontMedium;
t.Colour = entryColour;
});
if (entry.GithubUser.UserId != null)
title.AddUserLink(new User
{
Username = entry.GithubUser.OsuUsername,
Id = entry.GithubUser.UserId.Value
}, t => t.Font = fontMedium);
}, t =>
{
t.Font = fontMedium;
t.Colour = entryColour;
});
else if (entry.GithubUser.GithubUrl != null)
title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t => t.Font = fontMedium);
title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, Online.Chat.LinkAction.External, null, null, t =>
{
t.Font = fontMedium;
t.Colour = entryColour;
});
else
title.AddText(entry.GithubUser.DisplayName, t => t.Font = fontSmall);
title.AddText(entry.GithubUser.DisplayName, t =>
{
t.Font = fontSmall;
t.Colour = entryColour;
});
ChangelogEntries.Add(title);

View File

@ -90,8 +90,8 @@ namespace osu.Game.Overlays.Changelog
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleClick = audio.Sample.Get(@"UI/generic-select-soft");
sampleHover = audio.Sample.Get(@"UI/generic-hover-soft");
sampleClick = audio.Samples.Get(@"UI/generic-select-soft");
sampleHover = audio.Samples.Get(@"UI/generic-hover-soft");
}
protected override void OnActivated() => updateState();

View File

@ -76,7 +76,7 @@ namespace osu.Game.Overlays
},
};
sampleBack = audio.Sample.Get(@"UI/generic-select-soft");
sampleBack = audio.Samples.Get(@"UI/generic-select-soft");
header.Current.BindTo(Current);

View File

@ -3,6 +3,7 @@
using System;
using System.Linq;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@ -14,6 +15,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.Chat;
using osu.Game.Users;
using osuTK;
@ -201,6 +203,9 @@ namespace osu.Game.Overlays.Chat
private Action startChatAction;
[Resolved]
private IAPIProvider api { get; set; }
public MessageSender(User sender)
{
this.sender = sender;
@ -213,11 +218,21 @@ namespace osu.Game.Overlays.Chat
startChatAction = () => chatManager?.OpenPrivateChannel(sender);
}
public MenuItem[] ContextMenuItems => new MenuItem[]
public MenuItem[] ContextMenuItems
{
new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action),
new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction),
};
get
{
List<MenuItem> items = new List<MenuItem>
{
new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action)
};
if (sender.Id != api.LocalUser.Value.Id)
items.Add(new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction));
return items.ToArray();
}
}
}
private static readonly Color4[] username_colours =

View File

@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
@ -17,6 +19,11 @@ namespace osu.Game.Overlays
{
private Box overlay;
private readonly BindableDouble audioVolume = new BindableDouble(1);
[Resolved]
private AudioManager audio { get; set; }
[BackgroundDependencyLoader]
private void load()
{
@ -33,7 +40,19 @@ namespace osu.Game.Overlays
}
};
Progress.ValueChanged += p => overlay.Alpha = (float)p.NewValue;
Progress.ValueChanged += p =>
{
audioVolume.Value = 1 - p.NewValue;
overlay.Alpha = (float)p.NewValue;
};
audio.Tracks.AddAdjustment(AdjustableProperty.Volume, audioVolume);
}
protected override void Dispose(bool isDisposing)
{
audio.Tracks.RemoveAdjustment(AdjustableProperty.Volume, audioVolume);
base.Dispose(isDisposing);
}
}
}

View File

@ -145,7 +145,7 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader]
private void load(OsuColour colours, TextureStore textures, AudioManager audio)
{
getSample = audio.Sample.Get(@"MedalSplash/medal-get");
getSample = audio.Samples.Get(@"MedalSplash/medal-get");
innerSpin.Texture = outerSpin.Texture = textures.Get(@"MedalSplash/disc-spin");
disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters

View File

@ -1,40 +1,40 @@
// 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 osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Mods.Sections;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Overlays.Mods
{
public class ModSelectOverlay : WaveOverlayContainer
{
private const float content_width = 0.8f;
protected Color4 LowMultiplierColour, HighMultiplierColour;
protected readonly TriangleButton DeselectAllButton;
protected readonly OsuSpriteText MultiplierLabel, UnrankedLabel;
private readonly FillFlowContainer footerContainer;
protected readonly TriangleButton CloseButton;
protected readonly OsuSpriteText MultiplierLabel;
protected readonly OsuSpriteText UnrankedLabel;
protected override bool BlockNonPositionalInput => false;
@ -46,154 +46,14 @@ namespace osu.Game.Overlays.Mods
protected readonly IBindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, IBindable<RulesetInfo> ruleset, AudioManager audio, Bindable<IReadOnlyList<Mod>> mods)
{
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
UnrankedLabel.Colour = colours.Blue;
protected Color4 LowMultiplierColour;
protected Color4 HighMultiplierColour;
Ruleset.BindTo(ruleset);
if (mods != null) SelectedMods.BindTo(mods);
sampleOn = audio.Sample.Get(@"UI/check-on");
sampleOff = audio.Sample.Get(@"UI/check-off");
}
protected override void LoadComplete()
{
base.LoadComplete();
Ruleset.BindValueChanged(rulesetChanged, true);
SelectedMods.BindValueChanged(selectedModsChanged, true);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Ruleset.UnbindAll();
SelectedMods.UnbindAll();
}
private void rulesetChanged(ValueChangedEvent<RulesetInfo> e)
{
if (e.NewValue == null) return;
var instance = e.NewValue.CreateInstance();
foreach (ModSection section in ModSectionsContainer.Children)
section.Mods = instance.GetModsFor(section.ModType);
// attempt to re-select any already selected mods.
// this may be the first time we are receiving the ruleset, in which case they will still match.
selectedModsChanged(new ValueChangedEvent<IReadOnlyList<Mod>>(SelectedMods.Value, SelectedMods.Value));
// write the mods back to the SelectedMods bindable in the case a change was not applicable.
// this generally isn't required as the previous line will perform deselection; just here for safety.
refreshSelectedMods();
}
private void selectedModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> e)
{
foreach (ModSection section in ModSectionsContainer.Children)
section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList());
updateMods();
}
private void updateMods()
{
double multiplier = 1.0;
bool ranked = true;
foreach (Mod mod in SelectedMods.Value)
{
multiplier *= mod.ScoreMultiplier;
ranked &= mod.Ranked;
}
MultiplierLabel.Text = $"{multiplier:N2}x";
if (multiplier > 1.0)
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
else if (multiplier < 1.0)
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
else
MultiplierLabel.FadeColour(Color4.White, 200);
UnrankedLabel.FadeTo(ranked ? 0 : 1, 200);
}
protected override void PopOut()
{
base.PopOut();
footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
foreach (ModSection section in ModSectionsContainer.Children)
{
section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
}
}
protected override void PopIn()
{
base.PopIn();
footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
foreach (ModSection section in ModSectionsContainer.Children)
{
section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
}
}
public void DeselectAll()
{
foreach (ModSection section in ModSectionsContainer.Children)
section.DeselectAll();
refreshSelectedMods();
}
/// <summary>
/// Deselect one or more mods.
/// </summary>
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be deselected.</param>
/// <param name="immediate">Set to true to bypass animations and update selections immediately.</param>
public void DeselectTypes(Type[] modTypes, bool immediate = false)
{
if (modTypes.Length == 0) return;
foreach (ModSection section in ModSectionsContainer.Children)
section.DeselectTypes(modTypes, immediate);
}
private const float content_width = 0.8f;
private readonly FillFlowContainer footerContainer;
private SampleChannel sampleOn, sampleOff;
private void modButtonPressed(Mod selectedMod)
{
if (selectedMod != null)
{
if (State == Visibility.Visible) sampleOn?.Play();
DeselectTypes(selectedMod.IncompatibleMods, true);
}
else
{
if (State == Visibility.Visible) sampleOff?.Play();
}
refreshSelectedMods();
}
private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
public ModSelectOverlay()
{
Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2");
@ -364,6 +224,16 @@ namespace osu.Game.Overlays.Mods
Right = 20
}
},
CloseButton = new TriangleButton
{
Width = 180,
Text = "Close",
Action = Hide,
Margin = new MarginPadding
{
Right = 20
}
},
new OsuSpriteText
{
Text = @"Score Multiplier:",
@ -401,5 +271,171 @@ namespace osu.Game.Overlays.Mods
},
};
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, IBindable<RulesetInfo> ruleset, AudioManager audio, Bindable<IReadOnlyList<Mod>> mods)
{
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
UnrankedLabel.Colour = colours.Blue;
Ruleset.BindTo(ruleset);
if (mods != null) SelectedMods.BindTo(mods);
sampleOn = audio.Samples.Get(@"UI/check-on");
sampleOff = audio.Samples.Get(@"UI/check-off");
}
public void DeselectAll()
{
foreach (var section in ModSectionsContainer.Children)
section.DeselectAll();
refreshSelectedMods();
}
/// <summary>
/// Deselect one or more mods.
/// </summary>
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be deselected.</param>
/// <param name="immediate">Set to true to bypass animations and update selections immediately.</param>
public void DeselectTypes(Type[] modTypes, bool immediate = false)
{
if (modTypes.Length == 0) return;
foreach (var section in ModSectionsContainer.Children)
section.DeselectTypes(modTypes, immediate);
}
protected override void LoadComplete()
{
base.LoadComplete();
Ruleset.BindValueChanged(rulesetChanged, true);
SelectedMods.BindValueChanged(selectedModsChanged, true);
}
protected override void PopOut()
{
base.PopOut();
footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
foreach (var section in ModSectionsContainer.Children)
{
section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
}
}
protected override void PopIn()
{
base.PopIn();
footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
foreach (var section in ModSectionsContainer.Children)
{
section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
}
}
protected override bool OnKeyDown(KeyDownEvent e)
{
switch (e.Key)
{
case Key.Number1:
DeselectAllButton.Click();
return true;
case Key.Number2:
CloseButton.Click();
return true;
}
return base.OnKeyDown(e);
}
private void rulesetChanged(ValueChangedEvent<RulesetInfo> e)
{
if (e.NewValue == null) return;
var instance = e.NewValue.CreateInstance();
foreach (var section in ModSectionsContainer.Children)
section.Mods = instance.GetModsFor(section.ModType);
// attempt to re-select any already selected mods.
// this may be the first time we are receiving the ruleset, in which case they will still match.
selectedModsChanged(new ValueChangedEvent<IReadOnlyList<Mod>>(SelectedMods.Value, SelectedMods.Value));
// write the mods back to the SelectedMods bindable in the case a change was not applicable.
// this generally isn't required as the previous line will perform deselection; just here for safety.
refreshSelectedMods();
}
private void selectedModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> e)
{
foreach (var section in ModSectionsContainer.Children)
section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList());
updateMods();
}
private void updateMods()
{
var multiplier = 1.0;
var ranked = true;
foreach (var mod in SelectedMods.Value)
{
multiplier *= mod.ScoreMultiplier;
ranked &= mod.Ranked;
}
MultiplierLabel.Text = $"{multiplier:N2}x";
if (multiplier > 1.0)
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
else if (multiplier < 1.0)
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
else
MultiplierLabel.FadeColour(Color4.White, 200);
UnrankedLabel.FadeTo(ranked ? 0 : 1, 200);
}
private void modButtonPressed(Mod selectedMod)
{
if (selectedMod != null)
{
if (State == Visibility.Visible) sampleOn?.Play();
DeselectTypes(selectedMod.IncompatibleMods, true);
}
else
{
if (State == Visibility.Visible) sampleOff?.Play();
}
refreshSelectedMods();
}
private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
#region Disposal
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Ruleset.UnbindAll();
SelectedMods.UnbindAll();
}
#endregion
}
}

View File

@ -350,7 +350,7 @@ namespace osu.Game.Overlays
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
}
current.Track.Completed -= currentTrackCompleted;
//current.Track.Completed -= currentTrackCompleted;
}
current = beatmap.NewValue;

View File

@ -29,13 +29,11 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
protected override void ShowMore()
{
base.ShowMore();
request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage);
request.Success += sets => Schedule(() =>
{
ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
MoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0);
MoreButton.IsLoading = false;
if (!sets.Any() && VisiblePages == 1)
{

View File

@ -24,13 +24,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
protected override void ShowMore()
{
base.ShowMore();
request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
request.Success += beatmaps => Schedule(() =>
{
ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
MoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0);
MoreButton.IsLoading = false;
if (!beatmaps.Any() && VisiblePages == 1)
{

View File

@ -7,20 +7,17 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile.Sections
{
public class PaginatedContainer : FillFlowContainer
public abstract class PaginatedContainer : FillFlowContainer
{
protected readonly FillFlowContainer ItemsContainer;
protected readonly OsuHoverContainer ShowMoreButton;
protected readonly LoadingAnimation ShowMoreLoading;
protected readonly ShowMoreButton MoreButton;
protected readonly OsuSpriteText MissingText;
protected int VisiblePages;
@ -32,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections
protected APIRequest RetrievalRequest;
protected RulesetStore Rulesets;
public PaginatedContainer(Bindable<User> user, string header, string missing)
protected PaginatedContainer(Bindable<User> user, string header, string missing)
{
User.BindTo(user);
@ -45,38 +42,26 @@ namespace osu.Game.Overlays.Profile.Sections
new OsuSpriteText
{
Text = header,
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Regular, italics: true),
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold),
Margin = new MarginPadding { Top = 10, Bottom = 10 },
},
ItemsContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Bottom = 10 }
Spacing = new Vector2(0, 2),
},
ShowMoreButton = new OsuHoverContainer
MoreButton = new ShowMoreButton
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Alpha = 0,
Margin = new MarginPadding { Top = 10 },
Action = ShowMore,
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Child = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14),
Text = "show more",
Padding = new MarginPadding { Vertical = 10, Horizontal = 15 },
}
},
ShowMoreLoading = new LoadingAnimation
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(14),
},
MissingText = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14),
Font = OsuFont.GetFont(size: 15),
Text = missing,
Alpha = 0,
},
@ -97,16 +82,11 @@ namespace osu.Game.Overlays.Profile.Sections
{
VisiblePages = 0;
ItemsContainer.Clear();
ShowMoreButton.Hide();
if (e.NewValue != null)
ShowMore();
}
protected virtual void ShowMore()
{
ShowMoreLoading.Show();
ShowMoreButton.Hide();
}
protected abstract void ShowMore();
}
}

View File

@ -1,7 +1,6 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Users;
@ -9,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
@ -31,8 +31,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
protected override void ShowMore()
{
base.ShowMore();
request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage);
request.Success += scores => Schedule(() =>
{
@ -41,8 +39,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
if (!scores.Any() && VisiblePages == 1)
{
ShowMoreButton.Hide();
ShowMoreLoading.Hide();
MoreButton.Hide();
MoreButton.IsLoading = false;
MissingText.Show();
return;
}
@ -63,8 +61,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
LoadComponentsAsync(drawableScores, s =>
{
MissingText.Hide();
ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
MoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0);
MoreButton.IsLoading = false;
ItemsContainer.AddRange(s);
});

View File

@ -22,13 +22,11 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
protected override void ShowMore()
{
base.ShowMore();
request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
request.Success += activities => Schedule(() =>
{
ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
MoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0);
MoreButton.IsLoading = false;
if (!activities.Any() && VisiblePages == 1)
{

View File

@ -0,0 +1,146 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
using System.Collections.Generic;
namespace osu.Game.Overlays.Profile.Sections
{
public class ShowMoreButton : OsuHoverContainer
{
private const float fade_duration = 200;
private readonly Box background;
private readonly LoadingAnimation loading;
private readonly FillFlowContainer content;
protected override IEnumerable<Drawable> EffectTargets => new[] { background };
private bool isLoading;
public bool IsLoading
{
get => isLoading;
set
{
if (isLoading == value)
return;
isLoading = value;
Enabled.Value = !isLoading;
if (value)
{
loading.FadeIn(fade_duration, Easing.OutQuint);
content.FadeOut(fade_duration, Easing.OutQuint);
}
else
{
loading.FadeOut(fade_duration, Easing.OutQuint);
content.FadeIn(fade_duration, Easing.OutQuint);
}
}
}
public ShowMoreButton()
{
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new CircularContainer
{
Masking = true,
Size = new Vector2(140, 30),
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
content = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(7),
Children = new Drawable[]
{
new ChevronIcon(),
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
Text = "show more".ToUpper(),
},
new ChevronIcon(),
}
},
loading = new LoadingAnimation
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(12)
},
}
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colors)
{
IdleColour = colors.GreySeafoamDark;
HoverColour = colors.GreySeafoam;
}
protected override bool OnClick(ClickEvent e)
{
if (!Enabled.Value)
return false;
try
{
return base.OnClick(e);
}
finally
{
// run afterwards as this will disable this button.
IsLoading = true;
}
}
private class ChevronIcon : SpriteIcon
{
private const int bottom_margin = 2;
private const int icon_size = 8;
public ChevronIcon()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Margin = new MarginPadding { Bottom = bottom_margin };
Size = new Vector2(icon_size);
Icon = FontAwesome.Solid.ChevronDown;
}
[BackgroundDependencyLoader]
private void load(OsuColour colors)
{
Colour = colors.Yellow;
}
}
}
}

View File

@ -31,6 +31,11 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
LabelText = "Bypass caching (slow)",
Bindable = config.GetBindable<bool>(DebugSetting.BypassCaching)
},
new SettingsCheckbox
{
LabelText = "Bypass front-to-back render pass",
Bindable = config.GetBindable<bool>(DebugSetting.BypassFrontToBackPass)
}
};
}
}

View File

@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
private Bindable<ScalingMode> scalingMode;
private Bindable<Size> sizeFullscreen;
private readonly BindableList<WindowMode> windowModes = new BindableList<WindowMode>();
private readonly IBindableList<WindowMode> windowModes = new BindableList<WindowMode>();
private OsuGameBase game;
private SettingsDropdown<Size> resolutionDropdown;

View File

@ -31,6 +31,7 @@ namespace osu.Game.Overlays.Toolbar
public ToolbarRulesetSelector()
{
RelativeSizeAxes = Axes.Y;
AutoSizeAxes = Axes.X;
Children = new[]
{
@ -111,12 +112,6 @@ namespace osu.Game.Overlays.Toolbar
private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300);
protected override void Update()
{
base.Update();
Size = new Vector2(modeButtons.DrawSize.X, 1);
}
private void rulesetChanged(ValueChangedEvent<RulesetInfo> e)
{
foreach (ToolbarRulesetButton m in modeButtons.Children.Cast<ToolbarRulesetButton>())

View File

@ -179,7 +179,7 @@ namespace osu.Game.Rulesets.Mods
shader.GetUniform<Vector2>("flashlightSize").UpdateValue(ref flashlightSize);
shader.GetUniform<float>("flashlightDim").UpdateValue(ref flashlightDim);
Texture.WhitePixel.DrawQuad(screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction);
DrawQuad(Texture.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction);
shader.Unbind();
}

View File

@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.UI
/// <summary>
/// The playfield.
/// </summary>
public Playfield Playfield => playfield.Value;
public override Playfield Playfield => playfield.Value;
/// <summary>
/// Place to put drawables above hit objects but below UI.
@ -342,6 +342,11 @@ namespace osu.Game.Rulesets.UI
/// </summary>
public readonly BindableBool IsPaused = new BindableBool();
/// <summary>
/// The playfield.
/// </summary>
public abstract Playfield Playfield { get; }
/// <summary>
/// The frame-stable clock which is being used for playfield display.
/// </summary>

View File

@ -7,10 +7,7 @@ namespace osu.Game.Scoring
{
public enum ScoreRank
{
[Description(@"F")]
F,
[Description(@"F")]
[Description(@"D")]
D,
[Description(@"C")]

View File

@ -182,9 +182,9 @@ namespace osu.Game.Screens.Menu
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleHover = audio.Sample.Get(@"Menu/button-hover");
sampleHover = audio.Samples.Get(@"Menu/button-hover");
if (!string.IsNullOrEmpty(sampleName))
sampleClick = audio.Sample.Get($@"Menu/{sampleName}");
sampleClick = audio.Samples.Get($@"Menu/{sampleName}");
}
protected override bool OnMouseDown(MouseDownEvent e)

View File

@ -150,7 +150,7 @@ namespace osu.Game.Screens.Menu
if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle);
sampleBack = audio.Sample.Get(@"Menu/button-back-select");
sampleBack = audio.Samples.Get(@"Menu/button-back-select");
}
private void onMulti()

View File

@ -76,8 +76,8 @@ namespace osu.Game.Screens.Menu
introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
track = introBeatmap.Track;
welcome = audio.Sample.Get(@"welcome");
seeya = audio.Sample.Get(@"seeya");
welcome = audio.Samples.Get(@"welcome");
seeya = audio.Samples.Get(@"seeya");
}
private const double delay_step_one = 2300;

View File

@ -189,7 +189,6 @@ namespace osu.Game.Screens.Menu
base.Draw(vertexAction);
shader.Bind();
texture.TextureGL.Bind();
Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy;
@ -224,7 +223,8 @@ namespace osu.Game.Screens.Menu
Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix)
);
texture.DrawQuad(
DrawQuad(
texture,
rectangle,
colourInfo,
null,

View File

@ -255,8 +255,8 @@ namespace osu.Game.Screens.Menu
[BackgroundDependencyLoader]
private void load(TextureStore textures, AudioManager audio)
{
sampleClick = audio.Sample.Get(@"Menu/osu-logo-select");
sampleBeat = audio.Sample.Get(@"Menu/osu-logo-heartbeat");
sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat");
logo.Texture = textures.Get(@"Menu/logo");
ripple.Texture = textures.Get(@"Menu/logo");

View File

@ -137,7 +137,7 @@ namespace osu.Game.Screens.Multi.Match.Components
private class BackgroundSprite : UpdateableBeatmapBackgroundSprite
{
protected override double FadeDuration => 200;
protected override double TransformDuration => 200;
}
}
}

View File

@ -255,7 +255,7 @@ namespace osu.Game.Screens.Multi
if (!track.IsRunning)
{
game.Audio.AddItemToList(track);
game.Audio.AddItem(track);
track.Seek(Beatmap.Value.Metadata.PreviewTime);
track.Start();
}

View File

@ -99,7 +99,7 @@ namespace osu.Game.Screens
[BackgroundDependencyLoader(true)]
private void load(OsuGame osu, AudioManager audio)
{
sampleExit = audio.Sample.Get(@"UI/screen-back");
sampleExit = audio.Samples.Get(@"UI/screen-back");
}
public virtual bool OnPressed(GlobalAction action)

Some files were not shown because too many files have changed in this diff Show More