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

Merge branch 'master' into kudosu-section

This commit is contained in:
Dean Herbert 2017-11-23 17:19:42 +09:00 committed by GitHub
commit 9d481eba96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 1233 additions and 449 deletions

View File

@ -15,7 +15,9 @@ This is still heavily under development and is not intended for end-user use. Th
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted. We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted.
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu-framework/issues). Please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**.
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues).
Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible. Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible.

@ -1 +1 @@
Subproject commit 887db793c705b45071aea5e0c1cc931a7887165f Subproject commit c6fd2914926f2a6df23eda536c0310f072581b1b

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Net.Http;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Development; using osu.Framework.Development;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -198,10 +197,9 @@ namespace osu.Desktop.Overlays
} }
} }
} }
catch (HttpRequestException) catch (Exception)
{ {
//likely have no internet connection. // we'll ignore this and retry later. can be triggered by no internet connection or thread abortion.
//we'll ignore this and retry later.
} }
finally finally
{ {

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu
}; };
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[] public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
{ {
new BeatmapStatistic new BeatmapStatistic
{ {
Name = @"Circle count", Name = @"Circle count",

View File

@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
/// <summary> /// <summary>
/// Taiko fails at the end of the map if the player has not half-filled their HP bar. /// Taiko fails at the end of the map if the player has not half-filled their HP bar.
/// </summary> /// </summary>
protected override bool FailCondition => Hits == MaxHits && Health.Value <= 0.5; protected override bool DefaultFailCondition => Hits == MaxHits && Health.Value <= 0.5;
private double hpIncreaseTick; private double hpIncreaseTick;
private double hpIncreaseGreat; private double hpIncreaseGreat;

View File

@ -0,0 +1,27 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Screens.Edit.Components;
using osu.Game.Tests.Beatmaps;
using OpenTK;
namespace osu.Game.Tests.Visual
{
public class TestCasePlaybackControl : OsuTestCase
{
public TestCasePlaybackControl()
{
var playback = new PlaybackControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200,100)
};
playback.Beatmap.Value = new TestWorkingBeatmap(new Beatmap());
Add(playback);
}
}
}

View File

@ -36,7 +36,8 @@ namespace osu.Game.Tests.Visual
Username = @"peppy", Username = @"peppy",
Id = 2, Id = 2,
Country = new Country { FlagName = @"AU" }, Country = new Country { FlagName = @"AU" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
IsSupporter = true,
}) { Width = 300 }, }) { Width = 300 },
}, },
}); });

View File

@ -124,6 +124,7 @@
<Compile Include="Visual\TestCaseOnScreenDisplay.cs" /> <Compile Include="Visual\TestCaseOnScreenDisplay.cs" />
<Compile Include="Visual\TestCaseAllPlayers.cs" /> <Compile Include="Visual\TestCaseAllPlayers.cs" />
<Compile Include="Visual\TestCaseOsuGame.cs" /> <Compile Include="Visual\TestCaseOsuGame.cs" />
<Compile Include="Visual\TestCasePlaybackControl.cs" />
<Compile Include="Visual\TestCasePlaySongSelect.cs" /> <Compile Include="Visual\TestCasePlaySongSelect.cs" />
<Compile Include="Visual\TestCaseReplay.cs" /> <Compile Include="Visual\TestCaseReplay.cs" />
<Compile Include="Visual\TestCaseReplaySettingsOverlay.cs" /> <Compile Include="Visual\TestCaseReplaySettingsOverlay.cs" />

View File

@ -18,6 +18,7 @@ using osu.Framework.Platform;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO; using osu.Game.Beatmaps.IO;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.IPC; using osu.Game.IPC;
using osu.Game.Online.API; using osu.Game.Online.API;
@ -52,6 +53,11 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public event Action<BeatmapInfo> BeatmapRestored; public event Action<BeatmapInfo> BeatmapRestored;
/// <summary>
/// Fired when a beatmap download begins.
/// </summary>
public event Action<DownloadBeatmapSetRequest> BeatmapDownloadBegan;
/// <summary> /// <summary>
/// A default representation of a WorkingBeatmap to use when no beatmap is available. /// A default representation of a WorkingBeatmap to use when no beatmap is available.
/// </summary> /// </summary>
@ -165,7 +171,7 @@ namespace osu.Game.Beatmaps
catch (Exception e) catch (Exception e)
{ {
e = e.InnerException ?? e; e = e.InnerException ?? e;
Logger.Error(e, @"Could not import beatmap set"); Logger.Error(e, $@"Could not import beatmap set ({Path.GetFileName(path)})");
} }
} }
@ -221,21 +227,29 @@ namespace osu.Game.Beatmaps
/// Downloads a beatmap. /// Downloads a beatmap.
/// </summary> /// </summary>
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param> /// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
/// <returns>A new <see cref="DownloadBeatmapSetRequest"/>, or an existing one if a download is already in progress.</returns> /// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo) public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
{ {
var existing = GetExistingDownload(beatmapSetInfo); var existing = GetExistingDownload(beatmapSetInfo);
if (existing != null) return existing; if (existing != null || api == null) return;
if (api == null) return null; if (!api.LocalUser.Value.IsSupporter)
{
PostNotification?.Invoke(new SimpleNotification
{
Icon = FontAwesome.fa_superpowers,
Text = "You gotta be a supporter to download for now 'yo"
});
return;
}
ProgressNotification downloadNotification = new ProgressNotification ProgressNotification downloadNotification = new ProgressNotification
{ {
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}", Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
}; };
var request = new DownloadBeatmapSetRequest(beatmapSetInfo); var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);
request.DownloadProgressed += progress => request.DownloadProgressed += progress =>
{ {
@ -280,8 +294,7 @@ namespace osu.Game.Beatmaps
// don't run in the main api queue as this is a long-running task. // don't run in the main api queue as this is a long-running task.
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning); Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
BeatmapDownloadBegan?.Invoke(request);
return request;
} }
/// <summary> /// <summary>
@ -483,7 +496,8 @@ namespace osu.Game.Beatmaps
using (var stream = new StreamReader(reader.GetStream(mapName))) using (var stream = new StreamReader(reader.GetStream(mapName)))
metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
beatmapSet = new BeatmapSetInfo // check if a set already exists with the same online id.
beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo
{ {
OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, OnlineBeatmapSetID = metadata.OnlineBeatmapSetID,
Beatmaps = new List<BeatmapInfo>(), Beatmaps = new List<BeatmapInfo>(),
@ -510,16 +524,21 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID);
beatmap.BeatmapInfo.Metadata = null;
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); if (existing == null)
{
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null;
// TODO: this should be done in a better place once we actually need to dynamically update it. RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
beatmap.BeatmapInfo.Ruleset = ruleset;
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0;
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); // TODO: this should be done in a better place once we actually need to dynamically update it.
beatmap.BeatmapInfo.Ruleset = ruleset;
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0;
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
}
} }
} }

View File

@ -7,7 +7,7 @@ using osu.Framework.Graphics.Sprites;
namespace osu.Game.Beatmaps.Drawables namespace osu.Game.Beatmaps.Drawables
{ {
internal class BeatmapBackgroundSprite : Sprite public class BeatmapBackgroundSprite : Sprite
{ {
private readonly WorkingBeatmap working; private readonly WorkingBeatmap working;

View File

@ -11,7 +11,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -28,10 +27,8 @@ namespace osu.Game.Beatmaps.Drawables
public Action<BeatmapSetInfo> RestoreHiddenRequested; public Action<BeatmapSetInfo> RestoreHiddenRequested;
private readonly SpriteText title;
private readonly SpriteText artist;
private readonly WorkingBeatmap beatmap; private readonly WorkingBeatmap beatmap;
private readonly FillFlowContainer difficultyIcons; private readonly FillFlowContainer difficultyIcons;
public BeatmapSetHeader(WorkingBeatmap beatmap) public BeatmapSetHeader(WorkingBeatmap beatmap)
@ -41,6 +38,25 @@ namespace osu.Game.Beatmaps.Drawables
this.beatmap = beatmap; this.beatmap = beatmap;
difficultyIcons = new FillFlowContainer
{
Margin = new MarginPadding { Top = 5 },
AutoSizeAxes = Axes.Both,
};
}
protected override void Selected()
{
base.Selected();
GainedSelection?.Invoke(this);
}
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation)
{
if (localisation == null)
throw new ArgumentNullException(nameof(localisation));
Children = new Drawable[] Children = new Drawable[]
{ {
new DelayedLoadWrapper( new DelayedLoadWrapper(
@ -60,44 +76,26 @@ namespace osu.Game.Beatmaps.Drawables
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
title = new OsuSpriteText new OsuSpriteText
{ {
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title),
TextSize = 22, TextSize = 22,
Shadow = true, Shadow = true,
}, },
artist = new OsuSpriteText new OsuSpriteText
{ {
Font = @"Exo2.0-SemiBoldItalic", Font = @"Exo2.0-SemiBoldItalic",
Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist),
TextSize = 17, TextSize = 17,
Shadow = true, Shadow = true,
}, },
difficultyIcons = new FillFlowContainer difficultyIcons
{
Margin = new MarginPadding { Top = 5 },
AutoSizeAxes = Axes.Both,
}
} }
} }
}; };
} }
protected override void Selected()
{
base.Selected();
GainedSelection?.Invoke(this);
}
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation)
{
if (localisation == null)
throw new ArgumentNullException(nameof(localisation));
title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title);
artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist);
}
private class PanelBackground : BufferedContainer private class PanelBackground : BufferedContainer
{ {
public PanelBackground(WorkingBeatmap working) public PanelBackground(WorkingBeatmap working)

View File

@ -11,7 +11,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
internal class DummyWorkingBeatmap : WorkingBeatmap public class DummyWorkingBeatmap : WorkingBeatmap
{ {
private readonly OsuGameBase game; private readonly OsuGameBase game;

View File

@ -8,6 +8,7 @@ using osu.Game.Rulesets.Mods;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
@ -29,10 +30,10 @@ namespace osu.Game.Beatmaps
Mods.ValueChanged += mods => applyRateAdjustments(); Mods.ValueChanged += mods => applyRateAdjustments();
beatmap = new Lazy<Beatmap>(populateBeatmap); beatmap = new AsyncLazy<Beatmap>(populateBeatmap);
background = new Lazy<Texture>(populateBackground); background = new AsyncLazy<Texture>(populateBackground);
track = new Lazy<Track>(populateTrack); track = new AsyncLazy<Track>(populateTrack);
waveform = new Lazy<Waveform>(populateWaveform); waveform = new AsyncLazy<Waveform>(populateWaveform);
} }
protected abstract Beatmap GetBeatmap(); protected abstract Beatmap GetBeatmap();
@ -41,8 +42,10 @@ namespace osu.Game.Beatmaps
protected virtual Waveform GetWaveform() => new Waveform(); protected virtual Waveform GetWaveform() => new Waveform();
public bool BeatmapLoaded => beatmap.IsValueCreated; public bool BeatmapLoaded => beatmap.IsValueCreated;
public Beatmap Beatmap => beatmap.Value; public Beatmap Beatmap => beatmap.Value.Result;
private readonly Lazy<Beatmap> beatmap; public async Task<Beatmap> GetBeatmapAsync() => await beatmap.Value;
private readonly AsyncLazy<Beatmap> beatmap;
private Beatmap populateBeatmap() private Beatmap populateBeatmap()
{ {
@ -55,14 +58,16 @@ namespace osu.Game.Beatmaps
} }
public bool BackgroundLoaded => background.IsValueCreated; public bool BackgroundLoaded => background.IsValueCreated;
public Texture Background => background.Value; public Texture Background => background.Value.Result;
private Lazy<Texture> background; public async Task<Texture> GetBackgroundAsync() => await background.Value;
private AsyncLazy<Texture> background;
private Texture populateBackground() => GetBackground(); private Texture populateBackground() => GetBackground();
public bool TrackLoaded => track.IsValueCreated; public bool TrackLoaded => track.IsValueCreated;
public Track Track => track.Value; public Track Track => track.Value.Result;
private Lazy<Track> track; public async Task<Track> GetTrackAsync() => await track.Value;
private AsyncLazy<Track> track;
private Track populateTrack() private Track populateTrack()
{ {
@ -73,8 +78,9 @@ namespace osu.Game.Beatmaps
} }
public bool WaveformLoaded => waveform.IsValueCreated; public bool WaveformLoaded => waveform.IsValueCreated;
public Waveform Waveform => waveform.Value; public Waveform Waveform => waveform.Value.Result;
private readonly Lazy<Waveform> waveform; public async Task<Waveform> GetWaveformAsync() => await waveform.Value;
private readonly AsyncLazy<Waveform> waveform;
private Waveform populateWaveform() => GetWaveform(); private Waveform populateWaveform() => GetWaveform();
@ -107,5 +113,13 @@ namespace osu.Game.Beatmaps
foreach (var mod in Mods.Value.OfType<IApplicableToClock>()) foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(t); mod.ApplyToClock(t);
} }
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<T> valueFactory)
: base(() => Task.Run(valueFactory))
{
}
}
} }
} }

View File

@ -78,6 +78,7 @@ namespace osu.Game.Database
{ {
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.OnlineBeatmapID).IsUnique();
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.MD5Hash).IsUnique(); modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.MD5Hash).IsUnique();
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.Hash).IsUnique(); modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.Hash).IsUnique();

View File

@ -45,8 +45,8 @@ namespace osu.Game.Graphics.Containers
double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime; double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime;
TimingControlPoint timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
EffectControlPoint effectPoint = Beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
if (timingPoint.BeatLength == 0) if (timingPoint.BeatLength == 0)
return; return;
@ -67,7 +67,7 @@ namespace osu.Game.Graphics.Containers
return; return;
using (BeginDelayedSequence(-TimeSinceLastBeat, true)) using (BeginDelayedSequence(-TimeSinceLastBeat, true))
OnNewBeat(beatIndex, timingPoint, effectPoint, Beatmap.Value.Track.CurrentAmplitudes); OnNewBeat(beatIndex, timingPoint, effectPoint, track.CurrentAmplitudes);
lastBeat = beatIndex; lastBeat = beatIndex;
lastTimingPoint = timingPoint; lastTimingPoint = timingPoint;

View File

@ -66,7 +66,7 @@ namespace osu.Game.Graphics.UserInterface
} }
private float textSize = 28; private float textSize = 28;
internal float TextSize public float TextSize
{ {
get get
{ {

View File

@ -38,7 +38,7 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{ {
if (args.Key == Key.Escape) if (!args.Repeat && args.Key == Key.Escape)
{ {
if (Text.Length > 0) if (Text.Length > 0)
Text = string.Empty; Text = string.Empty;

View File

@ -11,7 +11,7 @@ using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume namespace osu.Game.Graphics.UserInterface.Volume
{ {
internal class VolumeControl : OverlayContainer public class VolumeControl : OverlayContainer
{ {
private readonly VolumeMeter volumeMeterMaster; private readonly VolumeMeter volumeMeterMaster;

View File

@ -8,7 +8,7 @@ using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume namespace osu.Game.Graphics.UserInterface.Volume
{ {
internal class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction> public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>
{ {
public Func<GlobalAction, bool> ActionRequested; public Func<GlobalAction, bool> ActionRequested;

View File

@ -13,7 +13,7 @@ using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume namespace osu.Game.Graphics.UserInterface.Volume
{ {
internal class VolumeMeter : Container, IKeyBindingHandler<GlobalAction> public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
{ {
private readonly Box meterFill; private readonly Box meterFill;
public BindableDouble Bindable { get; } = new BindableDouble(); public BindableDouble Bindable { get; } = new BindableDouble();

View File

@ -0,0 +1,302 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using osu.Game.Database;
using System;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20171119065731_AddBeatmapOnlineIDUniqueConstraint")]
partial class AddBeatmapOnlineIDUniqueConstraint
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
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<float>("SliderMultiplier");
b.Property<float>("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<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")
.IsUnique();
b.HasIndex("MD5Hash")
.IsUnique();
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.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
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("Variant");
b.ToTable("KeyBinding");
});
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.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("Available");
b.ToTable("RulesetInfo");
});
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");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace osu.Game.Migrations
{
public partial class AddBeatmapOnlineIDUniqueConstraint : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateIndex(
name: "IX_BeatmapInfo_OnlineBeatmapID",
table: "BeatmapInfo",
column: "OnlineBeatmapID",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_BeatmapInfo_OnlineBeatmapID",
table: "BeatmapInfo");
}
}
}

View File

@ -103,6 +103,9 @@ namespace osu.Game.Migrations
b.HasIndex("MetadataID"); b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID"); b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo"); b.ToTable("BeatmapInfo");

View File

@ -6,7 +6,7 @@ using osu.Framework.IO.Network;
namespace osu.Game.Online.API namespace osu.Game.Online.API
{ {
internal class OAuth public class OAuth
{ {
private readonly string clientId; private readonly string clientId;
private readonly string clientSecret; private readonly string clientSecret;

View File

@ -8,7 +8,7 @@ using Newtonsoft.Json;
namespace osu.Game.Online.API namespace osu.Game.Online.API
{ {
[Serializable] [Serializable]
internal class OAuthToken public class OAuthToken
{ {
/// <summary> /// <summary>
/// OAuth 2.0 access token. /// OAuth 2.0 access token.

View File

@ -12,13 +12,16 @@ namespace osu.Game.Online.API.Requests
public Action<float> DownloadProgressed; public Action<float> DownloadProgressed;
public DownloadBeatmapSetRequest(BeatmapSetInfo set) private readonly bool noVideo;
public DownloadBeatmapSetRequest(BeatmapSetInfo set, bool noVideo)
{ {
this.noVideo = noVideo;
BeatmapSet = set; BeatmapSet = set;
Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total); Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total);
} }
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download"; protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";
} }
} }

View File

@ -100,7 +100,7 @@ namespace osu.Game.Online.Multiplayer
} }
} }
internal class VersusRow : FillFlowContainer public class VersusRow : FillFlowContainer
{ {
public VersusRow(Color4 first, Color4 second, float size) public VersusRow(Color4 first, Color4 second, float size)
{ {

View File

@ -109,7 +109,7 @@ namespace osu.Game
dependencies.Cache(this); dependencies.Cache(this);
configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset); configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset);
Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value); Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First();
Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0; Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0;
} }

View File

@ -29,8 +29,11 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Container noVideoButtons; private readonly Container noVideoButtons;
private readonly FillFlowContainer videoButtons; private readonly FillFlowContainer videoButtons;
private readonly AuthorInfo author; private readonly AuthorInfo author;
private readonly Container downloadButtonsContainer;
public Details Details; public Details Details;
private BeatmapManager beatmaps;
private DelayedLoadWrapper cover; private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker; public readonly BeatmapPicker Picker;
@ -48,6 +51,7 @@ namespace osu.Game.Overlays.BeatmapSet
title.Text = BeatmapSet.Metadata.Title; title.Text = BeatmapSet.Metadata.Title;
artist.Text = BeatmapSet.Metadata.Artist; artist.Text = BeatmapSet.Metadata.Artist;
downloadButtonsContainer.FadeIn();
noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration); noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration);
videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration); videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration);
@ -164,7 +168,7 @@ namespace osu.Game.Overlays.BeatmapSet
Children = new Drawable[] Children = new Drawable[]
{ {
new FavouriteButton(), new FavouriteButton(),
new Container downloadButtonsContainer = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
@ -174,7 +178,10 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Alpha = 0f, Alpha = 0f,
Child = new DownloadButton("Download", @""), Child = new DownloadButton("Download", @"")
{
Action = () => download(false),
},
}, },
videoButtons = new FillFlowContainer videoButtons = new FillFlowContainer
{ {
@ -183,8 +190,14 @@ namespace osu.Game.Overlays.BeatmapSet
Alpha = 0f, Alpha = 0f,
Children = new[] Children = new[]
{ {
new DownloadButton("Download", "with Video"), new DownloadButton("Download", "with Video")
new DownloadButton("Download", "without Video"), {
Action = () => download(false),
},
new DownloadButton("Download", "without Video")
{
Action = () => download(true),
},
}, },
}, },
}, },
@ -208,9 +221,39 @@ namespace osu.Game.Overlays.BeatmapSet
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours, BeatmapManager beatmaps)
{ {
tabsBg.Colour = colours.Gray3; tabsBg.Colour = colours.Gray3;
this.beatmaps = beatmaps;
beatmaps.BeatmapSetAdded += handleBeatmapAdd;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
beatmaps.BeatmapSetAdded -= handleBeatmapAdd;
}
private void handleBeatmapAdd(BeatmapSetInfo beatmap)
{
if (beatmap.OnlineBeatmapSetID == BeatmapSet.OnlineBeatmapSetID)
downloadButtonsContainer.FadeOut(transition_duration);
}
private void download(bool noVideo)
{
if (beatmaps.GetExistingDownload(BeatmapSet) != null)
{
downloadButtonsContainer.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine).Then();
return;
}
beatmaps.Download(BeatmapSet, noVideo);
} }
} }
} }

View File

@ -58,7 +58,7 @@ namespace osu.Game.Overlays
private readonly Box chatBackground; private readonly Box chatBackground;
private readonly Box tabBackground; private readonly Box tabBackground;
public Bindable<double> ChatHeight { get; internal set; } public Bindable<double> ChatHeight { get; set; }
private readonly Container channelSelectionContainer; private readonly Container channelSelectionContainer;
private readonly ChannelSelectionOverlay channelSelection; private readonly ChannelSelectionOverlay channelSelection;

View File

@ -16,9 +16,7 @@ using osu.Game.Graphics.Sprites;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Overlays.Notifications;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
@ -35,10 +33,8 @@ namespace osu.Game.Overlays.Direct
private Container content; private Container content;
private APIAccess api;
private ProgressBar progressBar; private ProgressBar progressBar;
private BeatmapManager beatmaps; private BeatmapManager beatmaps;
private NotificationOverlay notifications;
private BeatmapSetOverlay beatmapSetOverlay; private BeatmapSetOverlay beatmapSetOverlay;
public Track Preview => PlayButton.Preview; public Track Preview => PlayButton.Preview;
@ -71,11 +67,9 @@ namespace osu.Game.Overlays.Direct
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications, BeatmapSetOverlay beatmapSetOverlay) private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{ {
this.api = api;
this.beatmaps = beatmaps; this.beatmaps = beatmaps;
this.notifications = notifications;
this.beatmapSetOverlay = beatmapSetOverlay; this.beatmapSetOverlay = beatmapSetOverlay;
AddInternal(content = new Container AddInternal(content = new Container
@ -109,6 +103,14 @@ namespace osu.Game.Overlays.Direct
if (downloadRequest != null) if (downloadRequest != null)
attachDownload(downloadRequest); attachDownload(downloadRequest);
beatmaps.BeatmapDownloadBegan += attachDownload;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
beatmaps.BeatmapDownloadBegan -= attachDownload;
} }
protected override void Update() protected override void Update()
@ -151,16 +153,6 @@ namespace osu.Game.Overlays.Direct
protected void StartDownload() protected void StartDownload()
{ {
if (!api.LocalUser.Value.IsSupporter)
{
notifications.Post(new SimpleNotification
{
Icon = FontAwesome.fa_superpowers,
Text = "You gotta be a supporter to download for now 'yo"
});
return;
}
if (beatmaps.GetExistingDownload(SetInfo) != null) if (beatmaps.GetExistingDownload(SetInfo) != null)
{ {
// we already have an active download running. // we already have an active download running.
@ -172,13 +164,14 @@ namespace osu.Game.Overlays.Direct
return; return;
} }
var request = beatmaps.Download(SetInfo); beatmaps.Download(SetInfo);
attachDownload(request);
} }
private void attachDownload(DownloadBeatmapSetRequest request) private void attachDownload(DownloadBeatmapSetRequest request)
{ {
if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID)
return;
progressBar.FadeIn(400, Easing.OutQuint); progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);

View File

@ -19,7 +19,7 @@ using OpenTK.Input;
namespace osu.Game.Overlays.KeyBinding namespace osu.Game.Overlays.KeyBinding
{ {
internal class KeyBindingRow : Container, IFilterable public class KeyBindingRow : Container, IFilterable
{ {
private readonly object action; private readonly object action;
private readonly IEnumerable<Framework.Input.Bindings.KeyBinding> bindings; private readonly IEnumerable<Framework.Input.Bindings.KeyBinding> bindings;

View File

@ -55,7 +55,7 @@ namespace osu.Game.Overlays.KeyBinding
} }
} }
internal class ResetButton : OsuButton public class ResetButton : OsuButton
{ {
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)

View File

@ -13,7 +13,7 @@ using osu.Game.Graphics.Cursor;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
internal class LoginOverlay : OsuFocusedOverlayContainer public class LoginOverlay : OsuFocusedOverlayContainer
{ {
private LoginSettings settingsSection; private LoginSettings settingsSection;

View File

@ -13,7 +13,7 @@ using System;
namespace osu.Game.Overlays.Music namespace osu.Game.Overlays.Music
{ {
internal class FilterControl : Container public class FilterControl : Container
{ {
public readonly FilterTextBox Search; public readonly FilterTextBox Search;

View File

@ -17,7 +17,7 @@ using OpenTK;
namespace osu.Game.Overlays.Music namespace osu.Game.Overlays.Music
{ {
internal class PlaylistItem : Container, IFilterable, IDraggable public class PlaylistItem : Container, IFilterable, IDraggable
{ {
private const float fade_duration = 100; private const float fade_duration = 100;

View File

@ -14,7 +14,7 @@ using OpenTK;
namespace osu.Game.Overlays.Music namespace osu.Game.Overlays.Music
{ {
internal class PlaylistList : CompositeDrawable public class PlaylistList : CompositeDrawable
{ {
public Action<BeatmapSetInfo> OnSelect; public Action<BeatmapSetInfo> OnSelect;

View File

@ -37,7 +37,7 @@ namespace osu.Game.Overlays
private const float bottom_black_area_height = 55; private const float bottom_black_area_height = 55;
private Drawable currentBackground; private Drawable background;
private ProgressBar progressBar; private ProgressBar progressBar;
private IconButton prevButton; private IconButton prevButton;
@ -120,7 +120,7 @@ namespace osu.Game.Overlays
}, },
Children = new[] Children = new[]
{ {
currentBackground = new Background(), background = new Background(),
title = new OsuSpriteText title = new OsuSpriteText
{ {
Origin = Anchor.BottomCentre, Origin = Anchor.BottomCentre,
@ -334,6 +334,7 @@ namespace osu.Game.Overlays
pendingBeatmapSwitch = Schedule(delegate pendingBeatmapSwitch = Schedule(delegate
{ {
// todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
Task.Run(() => Task.Run(() =>
{ {
if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists
@ -352,29 +353,26 @@ namespace osu.Game.Overlays
} }
}); });
playerContainer.Add(new AsyncLoadWrapper(new Background(beatmap) LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground =>
{ {
OnLoadComplete = newBackground => switch (direction)
{ {
switch (direction) case TransformDirection.Next:
{ newBackground.Position = new Vector2(400, 0);
case TransformDirection.Next: newBackground.MoveToX(0, 500, Easing.OutCubic);
newBackground.Position = new Vector2(400, 0); background.MoveToX(-400, 500, Easing.OutCubic);
newBackground.MoveToX(0, 500, Easing.OutCubic); break;
currentBackground.MoveToX(-400, 500, Easing.OutCubic); case TransformDirection.Prev:
break; newBackground.Position = new Vector2(-400, 0);
case TransformDirection.Prev: newBackground.MoveToX(0, 500, Easing.OutCubic);
newBackground.Position = new Vector2(-400, 0); background.MoveToX(400, 500, Easing.OutCubic);
newBackground.MoveToX(0, 500, Easing.OutCubic); break;
currentBackground.MoveToX(400, 500, Easing.OutCubic);
break;
}
currentBackground.Expire();
currentBackground = newBackground;
} }
})
{ background.Expire();
Depth = float.MaxValue, background = newBackground;
playerContainer.Add(newBackground);
}); });
}); });
} }

View File

@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide(); ShowMoreLoading.Hide();
if (!sets.Any()) if (!sets.Any() && VisiblePages == 1)
{ {
MissingText.Show(); MissingText.Show();
return; return;

View File

@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0); ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide(); ShowMoreLoading.Hide();
if (!scores.Any()) if (!scores.Any() && VisiblePages == 1)
{ {
MissingText.Show(); MissingText.Show();
return; return;

View File

@ -135,7 +135,7 @@ namespace osu.Game.Overlays.Settings
private class RestoreDefaultValueButton : Box, IHasTooltip private class RestoreDefaultValueButton : Box, IHasTooltip
{ {
private Bindable<T> bindable; private Bindable<T> bindable;
internal Bindable<T> Bindable public Bindable<T> Bindable
{ {
get { return bindable; } get { return bindable; }
set set
@ -185,13 +185,13 @@ namespace osu.Game.Overlays.Settings
UpdateState(); UpdateState();
} }
internal void SetButtonColour(Color4 buttonColour) public void SetButtonColour(Color4 buttonColour)
{ {
this.buttonColour = buttonColour; this.buttonColour = buttonColour;
UpdateState(); UpdateState();
} }
internal void UpdateState() public void UpdateState()
{ {
if (bindable == null) if (bindable == null)
return; return;

View File

@ -7,7 +7,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Settings namespace osu.Game.Overlays.Settings
{ {
internal class SettingsLabel : SettingsItem<string> public class SettingsLabel : SettingsItem<string>
{ {
protected override Drawable CreateControl() => null; protected override Drawable CreateControl() => null;

View File

@ -18,8 +18,8 @@ namespace osu.Game.Overlays.Settings
public class Sidebar : Container<SidebarButton>, IStateful<ExpandedState> public class Sidebar : Container<SidebarButton>, IStateful<ExpandedState>
{ {
private readonly FillFlowContainer<SidebarButton> content; private readonly FillFlowContainer<SidebarButton> content;
internal const float DEFAULT_WIDTH = ToolbarButton.WIDTH; public const float DEFAULT_WIDTH = ToolbarButton.WIDTH;
internal const int EXPANDED_WIDTH = 200; public const int EXPANDED_WIDTH = 200;
public event Action<ExpandedState> StateChanged; public event Action<ExpandedState> StateChanged;

View File

@ -20,7 +20,7 @@ namespace osu.Game.Overlays
{ {
public abstract class SettingsOverlay : OsuFocusedOverlayContainer public abstract class SettingsOverlay : OsuFocusedOverlayContainer
{ {
internal const float CONTENT_MARGINS = 10; public const float CONTENT_MARGINS = 10;
public const float TRANSITION_LENGTH = 600; public const float TRANSITION_LENGTH = 600;

View File

@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarChatButton : ToolbarOverlayToggleButton public class ToolbarChatButton : ToolbarOverlayToggleButton
{ {
public ToolbarChatButton() public ToolbarChatButton()
{ {

View File

@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarDirectButton : ToolbarOverlayToggleButton public class ToolbarDirectButton : ToolbarOverlayToggleButton
{ {
public ToolbarDirectButton() public ToolbarDirectButton()
{ {

View File

@ -5,7 +5,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarHomeButton : ToolbarButton public class ToolbarHomeButton : ToolbarButton
{ {
public ToolbarHomeButton() public ToolbarHomeButton()
{ {

View File

@ -14,7 +14,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarModeSelector : Container public class ToolbarModeSelector : Container
{ {
private const float padding = 10; private const float padding = 10;

View File

@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarMusicButton : ToolbarOverlayToggleButton public class ToolbarMusicButton : ToolbarOverlayToggleButton
{ {
public ToolbarMusicButton() public ToolbarMusicButton()
{ {

View File

@ -7,7 +7,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarNotificationButton : ToolbarOverlayToggleButton public class ToolbarNotificationButton : ToolbarOverlayToggleButton
{ {
protected override Anchor TooltipAnchor => Anchor.TopRight; protected override Anchor TooltipAnchor => Anchor.TopRight;

View File

@ -9,7 +9,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarOverlayToggleButton : ToolbarButton public class ToolbarOverlayToggleButton : ToolbarButton
{ {
private readonly Box stateBackground; private readonly Box stateBackground;

View File

@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarSettingsButton : ToolbarOverlayToggleButton public class ToolbarSettingsButton : ToolbarOverlayToggleButton
{ {
public ToolbarSettingsButton() public ToolbarSettingsButton()
{ {

View File

@ -6,7 +6,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarSocialButton : ToolbarOverlayToggleButton public class ToolbarSocialButton : ToolbarOverlayToggleButton
{ {
public ToolbarSocialButton() public ToolbarSocialButton()
{ {

View File

@ -9,7 +9,7 @@ using OpenTK;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarUserArea : Container public class ToolbarUserArea : Container
{ {
public LoginOverlay LoginOverlay; public LoginOverlay LoginOverlay;
private ToolbarUserButton button; private ToolbarUserButton button;

View File

@ -12,7 +12,7 @@ using OpenTK.Graphics;
namespace osu.Game.Overlays.Toolbar namespace osu.Game.Overlays.Toolbar
{ {
internal class ToolbarUserButton : ToolbarButton, IOnlineComponent public class ToolbarUserButton : ToolbarButton, IOnlineComponent
{ {
private readonly UpdateableAvatar avatar; private readonly UpdateableAvatar avatar;

View File

@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Judgements
/// <summary> /// <summary>
/// The combo prior to this judgement occurring. /// The combo prior to this judgement occurring.
/// </summary> /// </summary>
internal int ComboAtJudgement; public int ComboAtJudgement;
/// <summary> /// <summary>
/// The highest combo achieved prior to this judgement occurring. /// The highest combo achieved prior to this judgement occurring.
/// </summary> /// </summary>
internal int HighestComboAtJudgement; public int HighestComboAtJudgement;
/// <summary> /// <summary>
/// Whether a successful hit occurred. /// Whether a successful hit occurred.
@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Judgements
/// The offset from a perfect hit at which this judgement occurred. /// The offset from a perfect hit at which this judgement occurred.
/// Populated when added via <see cref="DrawableHitObject{TObject}.AddJudgement"/>. /// Populated when added via <see cref="DrawableHitObject{TObject}.AddJudgement"/>.
/// </summary> /// </summary>
public double TimeOffset { get; internal set; } public double TimeOffset { get; set; }
/// <summary> /// <summary>
/// Whether the <see cref="Result"/> should affect the combo portion of the score. /// Whether the <see cref="Result"/> should affect the combo portion of the score.

View File

@ -0,0 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for mods that make general adjustments to score processor.
/// </summary>
public interface IApplicableToScoreProcessor
{
void ApplyToScoreProcessor(ScoreProcessor scoreProcessor);
}
}

View File

@ -1,6 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
public abstract class ModPerfect : ModSuddenDeath public abstract class ModPerfect : ModSuddenDeath
@ -8,5 +10,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Perfect"; public override string Name => "Perfect";
public override string ShortenedName => "PF"; public override string ShortenedName => "PF";
public override string Description => "SS or quit."; public override string Description => "SS or quit.";
protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1;
} }
} }

View File

@ -3,10 +3,11 @@
using System; using System;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
public abstract class ModSuddenDeath : Mod public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor
{ {
public override string Name => "Sudden Death"; public override string Name => "Sudden Death";
public override string ShortenedName => "SD"; public override string ShortenedName => "SD";
@ -16,5 +17,12 @@ namespace osu.Game.Rulesets.Mods
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
public override bool Ranked => true; public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) }; public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{
scoreProcessor.FailConditions += FailCondition;
}
protected virtual bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Combo.Value == 0;
} }
} }

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
/// <summary> /// <summary>
/// A HitObjectParser to parse legacy osu!catch Beatmaps. /// A HitObjectParser to parse legacy osu!catch Beatmaps.
/// </summary> /// </summary>
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{ {
protected override HitObject CreateHit(Vector2 position, bool newCombo) protected override HitObject CreateHit(Vector2 position, bool newCombo)
{ {

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <summary> /// <summary>
/// A HitObjectParser to parse legacy Beatmaps. /// A HitObjectParser to parse legacy Beatmaps.
/// </summary> /// </summary>
internal abstract class ConvertHitObjectParser : HitObjectParser public abstract class ConvertHitObjectParser : HitObjectParser
{ {
public override HitObject Parse(string text) public override HitObject Parse(string text)
{ {

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
/// <summary> /// <summary>
/// A HitObjectParser to parse legacy osu!mania Beatmaps. /// A HitObjectParser to parse legacy osu!mania Beatmaps.
/// </summary> /// </summary>
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{ {
protected override HitObject CreateHit(Vector2 position, bool newCombo) protected override HitObject CreateHit(Vector2 position, bool newCombo)
{ {

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
/// <summary> /// <summary>
/// A HitObjectParser to parse legacy osu! Beatmaps. /// A HitObjectParser to parse legacy osu! Beatmaps.
/// </summary> /// </summary>
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{ {
protected override HitObject CreateHit(Vector2 position, bool newCombo) protected override HitObject CreateHit(Vector2 position, bool newCombo)
{ {

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
/// <summary> /// <summary>
/// A HitObjectParser to parse legacy osu!taiko Beatmaps. /// A HitObjectParser to parse legacy osu!taiko Beatmaps.
/// </summary> /// </summary>
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{ {
protected override HitObject CreateHit(Vector2 position, bool newCombo) protected override HitObject CreateHit(Vector2 position, bool newCombo)
{ {

View File

@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Scoring
/// </summary> /// </summary>
public event Action<Judgement> NewJudgement; public event Action<Judgement> NewJudgement;
/// <summary>
/// Additional conditions on top of <see cref="DefaultFailCondition"/> that cause a failing state.
/// </summary>
public event Func<ScoreProcessor, bool> FailConditions;
/// <summary> /// <summary>
/// The current total score. /// The current total score.
/// </summary> /// </summary>
@ -72,9 +77,9 @@ namespace osu.Game.Rulesets.Scoring
public virtual bool HasFailed { get; private set; } public virtual bool HasFailed { get; private set; }
/// <summary> /// <summary>
/// The conditions for failing. /// The default conditions for failing.
/// </summary> /// </summary>
protected virtual bool FailCondition => Health.Value == Health.MinValue; protected virtual bool DefaultFailCondition => Health.Value == Health.MinValue;
protected ScoreProcessor() protected ScoreProcessor()
{ {
@ -121,7 +126,10 @@ namespace osu.Game.Rulesets.Scoring
/// </summary> /// </summary>
protected void UpdateFailed() protected void UpdateFailed()
{ {
if (HasFailed || !FailCondition) if (HasFailed)
return;
if (!DefaultFailCondition && FailConditions?.Invoke(this) != true)
return; return;
if (Failed?.Invoke() != false) if (Failed?.Invoke() != false)
@ -216,8 +224,8 @@ namespace osu.Game.Rulesets.Scoring
OnNewJudgement(judgement); OnNewJudgement(judgement);
updateScore(); updateScore();
NotifyNewJudgement(judgement);
UpdateFailed(); UpdateFailed();
NotifyNewJudgement(judgement);
} }
protected void RemoveJudgement(Judgement judgement) protected void RemoveJudgement(Judgement judgement)

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Timing
/// <summary> /// <summary>
/// A <see cref="ScrollingContainer"/> which scrolls linearly relative to the <see cref="MultiplierControlPoint"/> start time. /// A <see cref="ScrollingContainer"/> which scrolls linearly relative to the <see cref="MultiplierControlPoint"/> start time.
/// </summary> /// </summary>
internal class LinearScrollingContainer : ScrollingContainer public class LinearScrollingContainer : ScrollingContainer
{ {
private readonly MultiplierControlPoint controlPoint; private readonly MultiplierControlPoint controlPoint;

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
public HitObjectContainer HitObjects { get; protected set; } public HitObjectContainer HitObjects { get; protected set; }
internal Container<Drawable> ScaledContent; public Container<Drawable> ScaledContent;
/// <summary> /// <summary>
/// Whether we are currently providing the local user a gameplay cursor. /// Whether we are currently providing the local user a gameplay cursor.

View File

@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.UI
/// A visual representation of a <see cref="Rulesets.Ruleset"/>. /// A visual representation of a <see cref="Rulesets.Ruleset"/>.
/// </summary> /// </summary>
/// <param name="ruleset">The ruleset being repesented.</param> /// <param name="ruleset">The ruleset being repesented.</param>
internal RulesetContainer(Ruleset ruleset) protected RulesetContainer(Ruleset ruleset)
{ {
Ruleset = ruleset; Ruleset = ruleset;
} }

View File

@ -32,9 +32,7 @@ namespace osu.Game.Screens.Backgrounds
Schedule(() => Schedule(() =>
{ {
var newBackground = new BeatmapBackground(beatmap); LoadComponentAsync(new BeatmapBackground(beatmap), b =>
LoadComponentAsync(newBackground, delegate
{ {
float newDepth = 0; float newDepth = 0;
if (background != null) if (background != null)
@ -45,8 +43,8 @@ namespace osu.Game.Screens.Backgrounds
background.Expire(); background.Expire();
} }
newBackground.Depth = newDepth; b.Depth = newDepth;
Add(background = newBackground); Add(background = b);
background.BlurSigma = blurTarget; background.BlurSigma = blurTarget;
}); });
}); });

View File

@ -3,7 +3,7 @@
namespace osu.Game.Screens.Charts namespace osu.Game.Screens.Charts
{ {
internal class ChartInfo : ScreenWhiteBox public class ChartInfo : ScreenWhiteBox
{ {
} }
} }

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace osu.Game.Screens.Charts namespace osu.Game.Screens.Charts
{ {
internal class ChartListing : ScreenWhiteBox public class ChartListing : ScreenWhiteBox
{ {
protected override IEnumerable<Type> PossibleChildren => new[] { protected override IEnumerable<Type> PossibleChildren => new[] {
typeof(ChartInfo) typeof(ChartInfo)

View File

@ -3,7 +3,7 @@
namespace osu.Game.Screens.Direct namespace osu.Game.Screens.Direct
{ {
internal class OnlineListing : ScreenWhiteBox public class OnlineListing : ScreenWhiteBox
{ {
} }
} }

View File

@ -0,0 +1,50 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
namespace osu.Game.Screens.Edit.Components
{
public class BottomBarContainer : Container
{
private const float corner_radius = 5;
private const float contents_padding = 15;
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
protected Track Track => Beatmap.Value.Track;
private readonly Drawable background;
private readonly Container content;
protected override Container<Drawable> Content => content;
public BottomBarContainer()
{
Masking = true;
CornerRadius = corner_radius;
InternalChildren = new[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
content = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = contents_padding },
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray1;
}
}
}

View File

@ -0,0 +1,154 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Edit.Components
{
public class PlaybackControl : BottomBarContainer
{
private readonly IconButton playButton;
public PlaybackControl()
{
PlaybackTabControl tabs;
Children = new Drawable[]
{
playButton = new IconButton
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f),
Icon = FontAwesome.fa_play_circle_o,
Action = togglePause,
Padding = new MarginPadding { Left = 20 }
},
new OsuSpriteText
{
Origin = Anchor.BottomLeft,
Text = "Playback Speed",
RelativePositionAxes = Axes.Y,
Y = 0.5f,
Padding = new MarginPadding { Left = 45 }
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Padding = new MarginPadding { Left = 45 },
Child = tabs = new PlaybackTabControl(),
}
};
tabs.Current.ValueChanged += newValue => Track.Tempo.Value = newValue;
}
private void togglePause()
{
if (Track.IsRunning)
Track.Stop();
else
Track.Start();
}
protected override void Update()
{
base.Update();
playButton.Icon = Track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
}
private class PlaybackTabControl : OsuTabControl<double>
{
private static readonly double[] tempo_values = { 0.5, 0.75, 1 };
protected override TabItem<double> CreateTabItem(double value) => new PlaybackTabItem(value);
protected override Dropdown<double> CreateDropdown() => null;
public PlaybackTabControl()
{
RelativeSizeAxes = Axes.Both;
TabContainer.Spacing = Vector2.Zero;
tempo_values.ForEach(AddItem);
}
public class PlaybackTabItem : TabItem<double>
{
private const float fade_duration = 200;
private readonly OsuSpriteText text;
private readonly OsuSpriteText textBold;
public PlaybackTabItem(double value) : base(value)
{
RelativeSizeAxes = Axes.Both;
Width = 1f / tempo_values.Length;
Children = new Drawable[]
{
text = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Text = $"{value:0%}",
TextSize = 14,
},
textBold = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Text = $"{value:0%}",
TextSize = 14,
Font = @"Exo2.0-Bold",
Alpha = 0,
},
};
}
private Color4 hoveredColour;
private Color4 normalColour;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
text.Colour = normalColour = colours.YellowDarker;
textBold.Colour = hoveredColour = colours.Yellow;
}
protected override bool OnHover(InputState state)
{
updateState();
return true;
}
protected override void OnHoverLost(InputState state) => updateState();
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
private void updateState()
{
text.FadeColour(Active || IsHovered ? hoveredColour : normalColour, fade_duration, Easing.OutQuint);
text.FadeTo(Active ? 0 : 1, fade_duration, Easing.OutQuint);
textBold.FadeTo(Active ? 1 : 0, fade_duration, Easing.OutQuint);
}
}
}
}
}

View File

@ -0,0 +1,38 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using System;
namespace osu.Game.Screens.Edit.Components
{
public class TimeInfoContainer : BottomBarContainer
{
private const int count_duration = 150;
private readonly OsuSpriteText trackTimer;
public TimeInfoContainer()
{
Children = new Drawable[]
{
trackTimer = new OsuSpriteText
{
Origin = Anchor.BottomLeft,
RelativePositionAxes = Axes.Y,
TextSize = 22,
FixedWidth = true,
Y = 0.5f,
}
};
}
protected override void Update()
{
base.Update();
trackTimer.Text = TimeSpan.FromMilliseconds(Track.CurrentTime).ToString(@"mm\:ss\:fff");
}
}
}

View File

@ -11,7 +11,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
/// <summary> /// <summary>
/// The part of the timeline that displays bookmarks. /// The part of the timeline that displays bookmarks.
/// </summary> /// </summary>
internal class BookmarkPart : TimelinePart public class BookmarkPart : TimelinePart
{ {
protected override void LoadBeatmap(WorkingBeatmap beatmap) protected override void LoadBeatmap(WorkingBeatmap beatmap)
{ {

View File

@ -12,7 +12,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
/// <summary> /// <summary>
/// The part of the timeline that displays breaks in the song. /// The part of the timeline that displays breaks in the song.
/// </summary> /// </summary>
internal class BreakPart : TimelinePart public class BreakPart : TimelinePart
{ {
protected override void LoadBeatmap(WorkingBeatmap beatmap) protected override void LoadBeatmap(WorkingBeatmap beatmap)
{ {

View File

@ -14,7 +14,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
/// <summary> /// <summary>
/// The part of the timeline that displays the control points. /// The part of the timeline that displays the control points.
/// </summary> /// </summary>
internal class ControlPointPart : TimelinePart public class ControlPointPart : TimelinePart
{ {
protected override void LoadBeatmap(WorkingBeatmap beatmap) protected override void LoadBeatmap(WorkingBeatmap beatmap)
{ {

View File

@ -15,7 +15,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
/// <summary> /// <summary>
/// The part of the timeline that displays the current position of the song. /// The part of the timeline that displays the current position of the song.
/// </summary> /// </summary>
internal class MarkerPart : TimelinePart public class MarkerPart : TimelinePart
{ {
private readonly Drawable marker; private readonly Drawable marker;

View File

@ -13,7 +13,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
/// <summary> /// <summary>
/// Represents a part of the summary timeline.. /// Represents a part of the summary timeline..
/// </summary> /// </summary>
internal abstract class TimelinePart : CompositeDrawable public abstract class TimelinePart : CompositeDrawable
{ {
public Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>(); public Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();

View File

@ -3,11 +3,9 @@
using OpenTK; using OpenTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
@ -16,83 +14,64 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
/// <summary> /// <summary>
/// The timeline that sits at the bottom of the editor. /// The timeline that sits at the bottom of the editor.
/// </summary> /// </summary>
public class SummaryTimeline : CompositeDrawable public class SummaryTimeline : BottomBarContainer
{ {
private const float corner_radius = 5;
private const float contents_padding = 15;
public Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private readonly Drawable background;
private readonly Drawable timelineBar; private readonly Drawable timelineBar;
public SummaryTimeline() public SummaryTimeline()
{ {
Masking = true;
CornerRadius = corner_radius;
TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart; TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart;
InternalChildren = new[] Children = new[]
{ {
background = new Box { RelativeSizeAxes = Axes.Both }, markerPart = new MarkerPart { RelativeSizeAxes = Axes.Both },
new Container controlPointPart = new ControlPointPart
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
bookmarkPart = new BookmarkPart
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
timelineBar = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = contents_padding, Right = contents_padding }, Children = new Drawable[]
Children = new[]
{ {
markerPart = new MarkerPart { RelativeSizeAxes = Axes.Both }, new Circle
controlPointPart = new ControlPointPart
{ {
Anchor = Anchor.Centre, Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomCentre, Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Both, Size = new Vector2(5)
Height = 0.35f
}, },
bookmarkPart = new BookmarkPart new Box
{ {
Anchor = Anchor.Centre, Anchor = Anchor.CentreLeft,
Origin = Anchor.TopCentre, Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.X,
Height = 0.35f Height = 1,
EdgeSmoothness = new Vector2(0, 1),
}, },
timelineBar = new Container new Circle
{ {
RelativeSizeAxes = Axes.Both, Anchor = Anchor.CentreRight,
Children = new Drawable[] Origin = Anchor.CentreLeft,
{ Size = new Vector2(5)
new Circle
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Size = new Vector2(5)
},
new Box
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
EdgeSmoothness = new Vector2(0, 1),
},
new Circle
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreLeft,
Size = new Vector2(5)
},
}
}, },
breakPart = new BreakPart
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.25f
}
} }
},
breakPart = new BreakPart
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.25f
} }
}; };
@ -105,7 +84,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
background.Colour = colours.Gray1;
timelineBar.Colour = colours.Gray5; timelineBar.Colour = colours.Gray5;
} }
} }

View File

@ -10,7 +10,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
/// <summary> /// <summary>
/// Represents a spanning point on a timeline part. /// Represents a spanning point on a timeline part.
/// </summary> /// </summary>
internal class DurationVisualisation : Container public class DurationVisualisation : Container
{ {
protected DurationVisualisation(double startTime, double endTime) protected DurationVisualisation(double startTime, double endTime)
{ {

View File

@ -10,7 +10,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
/// <summary> /// <summary>
/// Represents a singular point on a timeline part. /// Represents a singular point on a timeline part.
/// </summary> /// </summary>
internal class PointVisualisation : Box public class PointVisualisation : Box
{ {
protected PointVisualisation(double startTime) protected PointVisualisation(double startTime)
{ {

View File

@ -10,13 +10,13 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Screens.Edit.Menus; using osu.Game.Screens.Edit.Menus;
using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Game.Screens.Edit.Components.Timelines.Summary;
using OpenTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Screens; using osu.Game.Screens.Edit.Screens;
using osu.Game.Screens.Edit.Screens.Compose; using osu.Game.Screens.Edit.Screens.Compose;
using osu.Game.Screens.Edit.Screens.Design; using osu.Game.Screens.Edit.Screens.Design;
using osu.Game.Screens.Edit.Components;
namespace osu.Game.Screens.Edit namespace osu.Game.Screens.Edit
{ {
@ -34,7 +34,9 @@ namespace osu.Game.Screens.Edit
public Editor() public Editor()
{ {
EditorMenuBar menuBar; EditorMenuBar menuBar;
TimeInfoContainer timeInfo;
SummaryTimeline timeline; SummaryTimeline timeline;
PlaybackControl playback;
Children = new[] Children = new[]
{ {
@ -84,30 +86,47 @@ namespace osu.Game.Screens.Edit
new Container new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 5, Bottom = 5, Left = 10, Right = 10 }, Padding = new MarginPadding { Vertical = 5, Horizontal = 10 },
Child = new FillFlowContainer Child = new GridContainer
{ {
Name = "Bottom bar",
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal, ColumnDimensions = new[]
Spacing = new Vector2(10, 0),
Children = new[]
{ {
timeline = new SummaryTimeline new Dimension(GridSizeMode.Absolute, 220),
new Dimension(),
new Dimension(GridSizeMode.Absolute, 220)
},
Content = new[]
{
new Drawable[]
{ {
Anchor = Anchor.Centre, new Container
Origin = Anchor.Centre, {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Width = 0.65f Padding = new MarginPadding { Right = 10 },
} Child = timeInfo = new TimeInfoContainer { RelativeSizeAxes = Axes.Both },
},
timeline = new SummaryTimeline
{
RelativeSizeAxes = Axes.Both,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = 10 },
Child = playback = new PlaybackControl { RelativeSizeAxes = Axes.Both },
}
},
} }
} },
} }
} }
}, },
}; };
timeInfo.Beatmap.BindTo(Beatmap);
timeline.Beatmap.BindTo(Beatmap); timeline.Beatmap.BindTo(Beatmap);
playback.Beatmap.BindTo(Beatmap);
menuBar.Mode.ValueChanged += onModeChanged; menuBar.Mode.ValueChanged += onModeChanged;
} }
@ -154,7 +173,11 @@ namespace osu.Game.Screens.Edit
protected override bool OnExiting(Screen next) protected override bool OnExiting(Screen next)
{ {
Background.FadeColour(Color4.White, 500); Background.FadeColour(Color4.White, 500);
Beatmap.Value.Track?.Start(); if (Beatmap.Value.Track != null)
{
Beatmap.Value.Track.Tempo.Value = 1;
Beatmap.Value.Track.Start();
}
return base.OnExiting(next); return base.OnExiting(next);
} }
} }

View File

@ -9,7 +9,7 @@ using OpenTK.Graphics;
namespace osu.Game.Screens.Edit.Screens.Design namespace osu.Game.Screens.Edit.Screens.Design
{ {
internal class Design : EditorScreen public class Design : EditorScreen
{ {
public Design() public Design()
{ {

View File

@ -17,6 +17,7 @@ using OpenTK.Graphics;
using OpenTK.Input; using OpenTK.Input;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Threading;
namespace osu.Game.Screens.Menu namespace osu.Game.Screens.Menu
{ {
@ -38,10 +39,10 @@ namespace osu.Game.Screens.Menu
private readonly FlowContainerWithOrigin buttonFlow; private readonly FlowContainerWithOrigin buttonFlow;
//todo: make these non-internal somehow. //todo: make these non-internal somehow.
internal const float BUTTON_AREA_HEIGHT = 100; public const float BUTTON_AREA_HEIGHT = 100;
internal const float BUTTON_WIDTH = 140f; public const float BUTTON_WIDTH = 140f;
internal const float WEDGE_WIDTH = 20; public const float WEDGE_WIDTH = 20;
private OsuLogo logo; private OsuLogo logo;
@ -55,6 +56,8 @@ namespace osu.Game.Screens.Menu
// osuLogo.SizeForFlow relies on loading to be complete. // osuLogo.SizeForFlow relies on loading to be complete.
buttonFlow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0); buttonFlow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0);
updateLogoState();
} }
} }
@ -217,29 +220,17 @@ namespace osu.Game.Screens.Menu
if (state == MenuState.TopLevel) if (state == MenuState.TopLevel)
buttonArea.FinishTransforms(true); buttonArea.FinishTransforms(true);
updateLogoState(lastState);
using (buttonArea.BeginDelayedSequence(lastState == MenuState.Initial ? 150 : 0, true)) using (buttonArea.BeginDelayedSequence(lastState == MenuState.Initial ? 150 : 0, true))
{ {
switch (state) switch (state)
{ {
case MenuState.Exit: case MenuState.Exit:
case MenuState.Initial: case MenuState.Initial:
trackingPosition = false;
buttonAreaBackground.ScaleTo(Vector2.One, 500, Easing.Out); buttonAreaBackground.ScaleTo(Vector2.One, 500, Easing.Out);
buttonArea.FadeOut(300); buttonArea.FadeOut(300);
logo?.Delay(150)
.Schedule(() =>
{
toolbar?.Hide();
logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.Both;
logo.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo);
logo.ScaleTo(1, 800, Easing.OutExpo);
});
foreach (Button b in buttonsTopLevel) foreach (Button b in buttonsTopLevel)
b.State = ButtonState.Contracted; b.State = ButtonState.Contracted;
@ -252,33 +243,6 @@ namespace osu.Game.Screens.Menu
case MenuState.TopLevel: case MenuState.TopLevel:
buttonAreaBackground.ScaleTo(Vector2.One, 200, Easing.Out); buttonAreaBackground.ScaleTo(Vector2.One, 200, Easing.Out);
logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.None;
trackingPosition = true;
switch (lastState)
{
case MenuState.Initial:
logo.ScaleTo(0.5f, 200, Easing.In);
trackingPosition = false;
logo
.MoveTo(iconTrackingPosition, lastState == MenuState.EnteringMode ? 0 : 200, Easing.In)
.OnComplete(o =>
{
trackingPosition = true;
o.Impact();
toolbar?.Show();
});
break;
default:
logo.ScaleTo(0.5f, 200, Easing.OutQuint);
break;
}
buttonArea.FadeIn(300); buttonArea.FadeIn(300);
foreach (Button b in buttonsTopLevel) foreach (Button b in buttonsTopLevel)
@ -297,8 +261,6 @@ namespace osu.Game.Screens.Menu
case MenuState.EnteringMode: case MenuState.EnteringMode:
buttonAreaBackground.ScaleTo(new Vector2(2, 0), 300, Easing.InSine); buttonAreaBackground.ScaleTo(new Vector2(2, 0), 300, Easing.InSine);
trackingPosition = true;
buttonsTopLevel.ForEach(b => b.ContractStyle = 1); buttonsTopLevel.ForEach(b => b.ContractStyle = 1);
buttonsPlay.ForEach(b => b.ContractStyle = 1); buttonsPlay.ForEach(b => b.ContractStyle = 1);
backButton.ContractStyle = 1; backButton.ContractStyle = 1;
@ -320,9 +282,69 @@ namespace osu.Game.Screens.Menu
} }
} }
private Vector2 iconTrackingPosition => logo.Parent.ToLocalSpace(iconFacade.ScreenSpaceDrawQuad.Centre); private ScheduledDelegate logoDelayedAction;
private bool trackingPosition; private void updateLogoState(MenuState lastState = MenuState.Initial)
{
if (logo == null) return;
logoDelayedAction?.Cancel();
switch (state)
{
case MenuState.Exit:
case MenuState.Initial:
logoTracking = false;
logoDelayedAction = Scheduler.AddDelayed(() =>
{
toolbar?.Hide();
logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.Both;
logo.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo);
logo.ScaleTo(1, 800, Easing.OutExpo);
}, 150);
break;
case MenuState.TopLevel:
case MenuState.Play:
logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.None;
switch (lastState)
{
case MenuState.TopLevel: // coming from toplevel to play
case MenuState.Initial:
logoTracking = false;
logo.ScaleTo(0.5f, 200, Easing.In);
logo.MoveTo(logoTrackingPosition, lastState == MenuState.EnteringMode ? 0 : 200, Easing.In);
logoDelayedAction = Scheduler.AddDelayed(() =>
{
logoTracking = true;
logo.Impact();
toolbar?.Show();
}, 200);
break;
default:
logoTracking = true;
logo.ScaleTo(0.5f, 200, Easing.OutQuint);
break;
}
break;
case MenuState.EnteringMode:
logoTracking = true;
break;
}
}
private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(iconFacade.ScreenSpaceDrawQuad.Centre);
private bool logoTracking;
protected override void Update() protected override void Update()
{ {
@ -333,8 +355,8 @@ namespace osu.Game.Screens.Menu
if (logo != null) if (logo != null)
{ {
if (trackingPosition) if (logoTracking)
logo.Position = iconTrackingPosition; logo.Position = logoTrackingPosition;
iconFacade.Width = logo.SizeForFlow * 0.5f; iconFacade.Width = logo.SizeForFlow * 0.5f;
} }

View File

@ -25,7 +25,7 @@ namespace osu.Game.Screens.Menu
/// <summary> /// <summary>
/// Whether we have loaded the menu previously. /// Whether we have loaded the menu previously.
/// </summary> /// </summary>
internal bool DidLoadMenu; public bool DidLoadMenu;
private MainMenu mainMenu; private MainMenu mainMenu;
private SampleChannel welcome; private SampleChannel welcome;

View File

@ -85,12 +85,11 @@ namespace osu.Game.Screens.Menu
private void updateAmplitudes() private void updateAmplitudes()
{ {
var track = beatmap.Value.Track; var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null;
var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null;
float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256]; float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256];
var effect = beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current);
for (int i = 0; i < bars_per_visualiser; i++) for (int i = 0; i < bars_per_visualiser; i++)
{ {
if (track?.IsRunning ?? false) if (track?.IsRunning ?? false)

View File

@ -226,7 +226,7 @@ namespace osu.Game.Screens.Menu
/// </summary> /// </summary>
/// <param name="action">The animation to be performed</param> /// <param name="action">The animation to be performed</param>
/// <param name="waitForPrevious">If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared.</param> /// <param name="waitForPrevious">If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared.</param>
internal void AppendAnimatingAction(Action action, bool waitForPrevious) public void AppendAnimatingAction(Action action, bool waitForPrevious)
{ {
Action runnableAction = () => Action runnableAction = () =>
{ {

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multiplayer
{ {
internal class Lobby : ScreenWhiteBox public class Lobby : ScreenWhiteBox
{ {
protected override IEnumerable<Type> PossibleChildren => new[] { protected override IEnumerable<Type> PossibleChildren => new[] {
typeof(MatchCreate), typeof(MatchCreate),

View File

@ -12,7 +12,7 @@ using osu.Framework.Graphics;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multiplayer
{ {
internal class Match : ScreenWhiteBox public class Match : ScreenWhiteBox
{ {
protected override IEnumerable<Type> PossibleChildren => new[] { protected override IEnumerable<Type> PossibleChildren => new[] {
typeof(MatchSongSelect), typeof(MatchSongSelect),

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multiplayer
{ {
internal class MatchCreate : ScreenWhiteBox public class MatchCreate : ScreenWhiteBox
{ {
protected override IEnumerable<Type> PossibleChildren => new[] { protected override IEnumerable<Type> PossibleChildren => new[] {
typeof(Match) typeof(Match)

View File

@ -227,6 +227,9 @@ namespace osu.Game.Screens.Play
// Bind ScoreProcessor to ourselves // Bind ScoreProcessor to ourselves
scoreProcessor.AllJudged += onCompletion; scoreProcessor.AllJudged += onCompletion;
scoreProcessor.Failed += onFail; scoreProcessor.Failed += onFail;
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToScoreProcessor>())
mod.ApplyToScoreProcessor(scoreProcessor);
} }
private void applyRateFromMods() private void applyRateFromMods()

View File

@ -14,7 +14,7 @@ using OpenTK.Graphics;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
internal class ResultsPage : Container public class ResultsPage : Container
{ {
protected readonly Score Score; protected readonly Score Score;
protected readonly WorkingBeatmap Beatmap; protected readonly WorkingBeatmap Beatmap;

View File

@ -12,7 +12,7 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
internal class ResultsPageRanking : ResultsPage public class ResultsPageRanking : ResultsPage
{ {
public ResultsPageRanking(Score score, WorkingBeatmap beatmap = null) : base(score, beatmap) public ResultsPageRanking(Score score, WorkingBeatmap beatmap = null) : base(score, beatmap)
{ {

View File

@ -26,7 +26,7 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Ranking namespace osu.Game.Screens.Ranking
{ {
internal class ResultsPageScore : ResultsPage public class ResultsPageScore : ResultsPage
{ {
private ScoreCounter scoreCounter; private ScoreCounter scoreCounter;
@ -324,7 +324,7 @@ namespace osu.Game.Screens.Ranking
title.Colour = artist.Colour = colours.BlueDarker; title.Colour = artist.Colour = colours.BlueDarker;
versionMapper.Colour = colours.Gray8; versionMapper.Colour = colours.Gray8;
versionMapper.Text = $"{beatmap.Version} - mapped by {beatmap.Metadata.Author}"; versionMapper.Text = $"{beatmap.Version} - mapped by {beatmap.Metadata.Author.Username}";
title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title); title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title);
artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist); artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist);
} }

View File

@ -23,7 +23,7 @@ using osu.Game.Graphics.Cursor;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select
{ {
internal class BeatmapCarousel : OsuScrollContainer public class BeatmapCarousel : OsuScrollContainer
{ {
public BeatmapInfo SelectedBeatmap => selectedPanel?.Beatmap; public BeatmapInfo SelectedBeatmap => selectedPanel?.Beatmap;
@ -116,7 +116,7 @@ namespace osu.Game.Screens.Select
Schedule(() => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID))); Schedule(() => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID)));
} }
internal void UpdateBeatmap(BeatmapInfo beatmap) public void UpdateBeatmap(BeatmapInfo beatmap)
{ {
// todo: this method should not run more than once for the same BeatmapSetInfo. // todo: this method should not run more than once for the same BeatmapSetInfo.
var set = manager.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID); var set = manager.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID);
@ -572,7 +572,18 @@ namespace osu.Game.Screens.Select
// Makes sure headers are always _below_ panels, // Makes sure headers are always _below_ panels,
// and depth flows downward. // and depth flows downward.
panel.Depth = i + (panel is BeatmapSetHeader ? panels.Count : 0); panel.Depth = i + (panel is BeatmapSetHeader ? panels.Count : 0);
scrollableContent.Add(panel);
switch (panel.LoadState)
{
case LoadState.NotLoaded:
LoadComponentAsync(panel);
break;
case LoadState.Loading:
break;
default:
scrollableContent.Add(panel);
break;
}
} }
} }

View File

@ -22,11 +22,11 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select
{ {
internal class BeatmapInfoWedge : OverlayContainer public class BeatmapInfoWedge : OverlayContainer
{ {
private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0); private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0);
private Drawable beatmapInfoContainer; private Drawable info;
public BeatmapInfoWedge() public BeatmapInfoWedge()
{ {
@ -43,12 +43,6 @@ namespace osu.Game.Screens.Select
}; };
} }
protected override void LoadComplete()
{
base.LoadComplete();
AlwaysPresent = true;
}
protected override bool BlockPassThroughMouse => false; protected override bool BlockPassThroughMouse => false;
protected override void PopIn() protected override void PopIn()
@ -65,56 +59,62 @@ namespace osu.Game.Screens.Select
public void UpdateBeatmap(WorkingBeatmap beatmap) public void UpdateBeatmap(WorkingBeatmap beatmap)
{ {
var lastContainer = beatmapInfoContainer; LoadComponentAsync(new BufferedWedgeInfo(beatmap)
float newDepth = lastContainer?.Depth + 1 ?? 0;
Add(beatmapInfoContainer = new AsyncLoadWrapper(
new BufferedWedgeInfo(beatmap)
{
Shear = -Shear,
OnLoadComplete = d =>
{
this.FadeIn(250);
lastContainer?.FadeOut(250);
lastContainer?.Expire();
}
})
{ {
Depth = newDepth, Shear = -Shear,
Depth = info?.Depth + 1 ?? 0,
}, newInfo =>
{
// ensure we ourselves are visible if not already.
if (!IsPresent)
this.FadeIn(250);
info?.FadeOut(250);
info?.Expire();
Add(info = newInfo);
}); });
} }
public class BufferedWedgeInfo : BufferedContainer public class BufferedWedgeInfo : BufferedContainer
{ {
public BufferedWedgeInfo(WorkingBeatmap beatmap) private readonly WorkingBeatmap working;
public BufferedWedgeInfo(WorkingBeatmap working)
{ {
BeatmapInfo beatmapInfo = beatmap.BeatmapInfo; this.working = working;
BeatmapMetadata metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); }
[BackgroundDependencyLoader]
private void load()
{
BeatmapInfo beatmapInfo = working.BeatmapInfo;
BeatmapMetadata metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
Beatmap beatmap = working.Beatmap;
List<InfoLabel> labels = new List<InfoLabel>(); List<InfoLabel> labels = new List<InfoLabel>();
if (beatmap.Beatmap != null) if (beatmap != null)
{ {
HitObject lastObject = beatmap.Beatmap.HitObjects.LastOrDefault(); HitObject lastObject = beatmap.HitObjects.LastOrDefault();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0; double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
labels.Add(new InfoLabel(new BeatmapStatistic labels.Add(new InfoLabel(new BeatmapStatistic
{ {
Name = "Length", Name = "Length",
Icon = FontAwesome.fa_clock_o, Icon = FontAwesome.fa_clock_o,
Content = beatmap.Beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"), Content = beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
})); }));
labels.Add(new InfoLabel(new BeatmapStatistic labels.Add(new InfoLabel(new BeatmapStatistic
{ {
Name = "BPM", Name = "BPM",
Icon = FontAwesome.fa_circle, Icon = FontAwesome.fa_circle,
Content = getBPMRange(beatmap.Beatmap), Content = getBPMRange(beatmap),
})); }));
//get statistics from the current ruleset. //get statistics from the current ruleset.
labels.AddRange(beatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(beatmap).Select(s => new InfoLabel(s))); labels.AddRange(beatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(working).Select(s => new InfoLabel(s)));
} }
PixelSnapping = true; PixelSnapping = true;
@ -140,7 +140,7 @@ namespace osu.Game.Screens.Select
Children = new[] Children = new[]
{ {
// Zoomed-in and cropped beatmap background // Zoomed-in and cropped beatmap background
new BeatmapBackgroundSprite(beatmap) new BeatmapBackgroundSprite(working)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
@ -149,7 +149,7 @@ namespace osu.Game.Screens.Select
}, },
}, },
}, },
new DifficultyColourBar(beatmap.BeatmapInfo) new DifficultyColourBar(beatmapInfo)
{ {
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Width = 20, Width = 20,
@ -206,13 +206,13 @@ namespace osu.Game.Screens.Select
Font = @"Exo2.0-Medium", Font = @"Exo2.0-Medium",
Text = "mapped by ", Text = "mapped by ",
TextSize = 15, TextSize = 15,
}, },
new OsuSpriteText new OsuSpriteText
{ {
Font = @"Exo2.0-Bold", Font = @"Exo2.0-Bold",
Text = metadata.Author.Username, Text = metadata.Author.Username,
TextSize = 15, TextSize = 15,
}, },
} }
}, },
new FillFlowContainer new FillFlowContainer
@ -244,38 +244,39 @@ namespace osu.Game.Screens.Select
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
Children = new Drawable[] Children = new Drawable[]
{ {
new SpriteIcon new SpriteIcon
{ {
Icon = FontAwesome.fa_square, Icon = FontAwesome.fa_square,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Colour = new Color4(68, 17, 136, 255), Colour = new Color4(68, 17, 136, 255),
Rotation = 45, Rotation = 45,
Size = new Vector2(20), Size = new Vector2(20),
}, },
new SpriteIcon new SpriteIcon
{ {
Icon = statistic.Icon, Icon = statistic.Icon,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Colour = new Color4(255, 221, 85, 255), Colour = new Color4(255, 221, 85, 255),
Scale = new Vector2(0.8f), Scale = new Vector2(0.8f),
Size = new Vector2(20), Size = new Vector2(20),
}, },
new OsuSpriteText new OsuSpriteText
{ {
Margin = new MarginPadding { Left = 13 }, Margin = new MarginPadding { Left = 13 },
Font = @"Exo2.0-Bold", Font = @"Exo2.0-Bold",
Colour = new Color4(255, 221, 85, 255), Colour = new Color4(255, 221, 85, 255),
Text = statistic.Content, Text = statistic.Content,
TextSize = 17, TextSize = 17,
Origin = Anchor.CentreLeft Origin = Anchor.CentreLeft
}, },
}; };
} }
} }
private class DifficultyColourBar : DifficultyColouredContainer private class DifficultyColourBar : DifficultyColouredContainer
{ {
public DifficultyColourBar(BeatmapInfo beatmap) : base(beatmap) public DifficultyColourBar(BeatmapInfo beatmap)
: base(beatmap)
{ {
} }
@ -286,21 +287,21 @@ namespace osu.Game.Screens.Select
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = AccentColour, Colour = AccentColour,
Width = full_opacity_ratio, Width = full_opacity_ratio,
}, },
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both, RelativePositionAxes = Axes.Both,
Colour = AccentColour, Colour = AccentColour,
Alpha = 0.5f, Alpha = 0.5f,
X = full_opacity_ratio, X = full_opacity_ratio,
Width = 1 - full_opacity_ratio, Width = 1 - full_opacity_ratio,
} }
}; };
} }
} }

View File

@ -40,20 +40,12 @@ namespace osu.Game.Screens.Select.Leaderboards
scores = value; scores = value;
getScoresRequest?.Cancel(); getScoresRequest?.Cancel();
int i = 150; scrollFlow?.FadeOut(200);
scrollFlow?.Expire();
scrollFlow = null;
if (scores == null) if (scores == null)
{
if (scrollFlow != null)
{
foreach (var c in scrollFlow.Children)
c.FadeOut(i += 10);
foreach (var c in scrollFlow.Children)
c.LifetimeEnd = Time.Current + i;
}
return; return;
}
// schedule because we may not be loaded yet (LoadComponentAsync complains). // schedule because we may not be loaded yet (LoadComponentAsync complains).
Schedule(() => Schedule(() =>
@ -64,13 +56,12 @@ namespace osu.Game.Screens.Select.Leaderboards
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 5f), Spacing = new Vector2(0f, 5f),
Padding = new MarginPadding { Top = 10, Bottom = 5 }, Padding = new MarginPadding { Top = 10, Bottom = 5 },
ChildrenEnumerable = scores.Select(s => new LeaderboardScore(s, 1 + i) { Action = () => ScoreSelected?.Invoke(s) }) ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
}, f => }, f =>
{ {
scrollFlow?.Expire();
scrollContainer.Add(scrollFlow = f); scrollContainer.Add(scrollFlow = f);
i = 0; int i = 0;
foreach (var s in f.Children) foreach (var s in f.Children)
{ {
using (s.BeginDelayedSequence(i++ * 50, true)) using (s.BeginDelayedSequence(i++ * 50, true))

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq; using System.Linq;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -24,8 +23,6 @@ namespace osu.Game.Screens.Select.Leaderboards
{ {
public static readonly float HEIGHT = 60; public static readonly float HEIGHT = 60;
public event Action<Visibility> StateChanged;
public readonly int RankPosition; public readonly int RankPosition;
public readonly Score Score; public readonly Score Score;

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