Merge branch 'master' into kudosu-section

This commit is contained in:
Dean Herbert
2017-11-23 17:19:42 +09:00
committed by GitHub
103 changed files with 1233 additions and 449 deletions

View File

@ -18,6 +18,7 @@ using osu.Framework.Platform;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.IO;
using osu.Game.IPC;
using osu.Game.Online.API;
@ -52,6 +53,11 @@ namespace osu.Game.Beatmaps
/// </summary>
public event Action<BeatmapInfo> BeatmapRestored;
/// <summary>
/// Fired when a beatmap download begins.
/// </summary>
public event Action<DownloadBeatmapSetRequest> BeatmapDownloadBegan;
/// <summary>
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
/// </summary>
@ -165,7 +171,7 @@ namespace osu.Game.Beatmaps
catch (Exception 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.
/// </summary>
/// <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>
public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo)
/// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
{
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
{
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
};
var request = new DownloadBeatmapSetRequest(beatmapSetInfo);
var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);
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.
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
return request;
BeatmapDownloadBegan?.Invoke(request);
}
/// <summary>
@ -483,7 +496,8 @@ namespace osu.Game.Beatmaps
using (var stream = new StreamReader(reader.GetStream(mapName)))
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,
Beatmaps = new List<BeatmapInfo>(),
@ -510,16 +524,21 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null;
var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID);
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.
beatmap.BeatmapInfo.Ruleset = ruleset;
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0;
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
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
{
internal class BeatmapBackgroundSprite : Sprite
public class BeatmapBackgroundSprite : Sprite
{
private readonly WorkingBeatmap working;

View File

@ -11,7 +11,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
@ -28,10 +27,8 @@ namespace osu.Game.Beatmaps.Drawables
public Action<BeatmapSetInfo> RestoreHiddenRequested;
private readonly SpriteText title;
private readonly SpriteText artist;
private readonly WorkingBeatmap beatmap;
private readonly FillFlowContainer difficultyIcons;
public BeatmapSetHeader(WorkingBeatmap beatmap)
@ -41,6 +38,25 @@ namespace osu.Game.Beatmaps.Drawables
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[]
{
new DelayedLoadWrapper(
@ -60,44 +76,26 @@ namespace osu.Game.Beatmaps.Drawables
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
title = new OsuSpriteText
new OsuSpriteText
{
Font = @"Exo2.0-BoldItalic",
Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title),
TextSize = 22,
Shadow = true,
},
artist = new OsuSpriteText
new OsuSpriteText
{
Font = @"Exo2.0-SemiBoldItalic",
Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist),
TextSize = 17,
Shadow = true,
},
difficultyIcons = new FillFlowContainer
{
Margin = new MarginPadding { Top = 5 },
AutoSizeAxes = Axes.Both,
}
difficultyIcons
}
}
};
}
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
{
public PanelBackground(WorkingBeatmap working)
@ -185,4 +183,4 @@ namespace osu.Game.Beatmaps.Drawables
}
}
}
}
}

View File

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

View File

@ -8,6 +8,7 @@ using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace osu.Game.Beatmaps
{
@ -29,10 +30,10 @@ namespace osu.Game.Beatmaps
Mods.ValueChanged += mods => applyRateAdjustments();
beatmap = new Lazy<Beatmap>(populateBeatmap);
background = new Lazy<Texture>(populateBackground);
track = new Lazy<Track>(populateTrack);
waveform = new Lazy<Waveform>(populateWaveform);
beatmap = new AsyncLazy<Beatmap>(populateBeatmap);
background = new AsyncLazy<Texture>(populateBackground);
track = new AsyncLazy<Track>(populateTrack);
waveform = new AsyncLazy<Waveform>(populateWaveform);
}
protected abstract Beatmap GetBeatmap();
@ -41,8 +42,10 @@ namespace osu.Game.Beatmaps
protected virtual Waveform GetWaveform() => new Waveform();
public bool BeatmapLoaded => beatmap.IsValueCreated;
public Beatmap Beatmap => beatmap.Value;
private readonly Lazy<Beatmap> beatmap;
public Beatmap Beatmap => beatmap.Value.Result;
public async Task<Beatmap> GetBeatmapAsync() => await beatmap.Value;
private readonly AsyncLazy<Beatmap> beatmap;
private Beatmap populateBeatmap()
{
@ -55,14 +58,16 @@ namespace osu.Game.Beatmaps
}
public bool BackgroundLoaded => background.IsValueCreated;
public Texture Background => background.Value;
private Lazy<Texture> background;
public Texture Background => background.Value.Result;
public async Task<Texture> GetBackgroundAsync() => await background.Value;
private AsyncLazy<Texture> background;
private Texture populateBackground() => GetBackground();
public bool TrackLoaded => track.IsValueCreated;
public Track Track => track.Value;
private Lazy<Track> track;
public Track Track => track.Value.Result;
public async Task<Track> GetTrackAsync() => await track.Value;
private AsyncLazy<Track> track;
private Track populateTrack()
{
@ -73,8 +78,9 @@ namespace osu.Game.Beatmaps
}
public bool WaveformLoaded => waveform.IsValueCreated;
public Waveform Waveform => waveform.Value;
private readonly Lazy<Waveform> waveform;
public Waveform Waveform => waveform.Value.Result;
public async Task<Waveform> GetWaveformAsync() => await waveform.Value;
private readonly AsyncLazy<Waveform> waveform;
private Waveform populateWaveform() => GetWaveform();
@ -107,5 +113,13 @@ namespace osu.Game.Beatmaps
foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
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);
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.OnlineBeatmapID).IsUnique();
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.MD5Hash).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;
TimingControlPoint timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
EffectControlPoint effectPoint = Beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
if (timingPoint.BeatLength == 0)
return;
@ -67,7 +67,7 @@ namespace osu.Game.Graphics.Containers
return;
using (BeginDelayedSequence(-TimeSinceLastBeat, true))
OnNewBeat(beatIndex, timingPoint, effectPoint, Beatmap.Value.Track.CurrentAmplitudes);
OnNewBeat(beatIndex, timingPoint, effectPoint, track.CurrentAmplitudes);
lastBeat = beatIndex;
lastTimingPoint = timingPoint;

View File

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

View File

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

View File

@ -11,7 +11,7 @@ using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume
{
internal class VolumeControl : OverlayContainer
public class VolumeControl : OverlayContainer
{
private readonly VolumeMeter volumeMeterMaster;
@ -119,4 +119,4 @@ namespace osu.Game.Graphics.UserInterface.Volume
this.Delay(1000).Schedule(Hide, out popOutDelegate);
}
}
}
}

View File

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

View File

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

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("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");

View File

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

View File

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

View File

@ -12,13 +12,16 @@ namespace osu.Game.Online.API.Requests
public Action<float> DownloadProgressed;
public DownloadBeatmapSetRequest(BeatmapSetInfo set)
private readonly bool noVideo;
public DownloadBeatmapSetRequest(BeatmapSetInfo set, bool noVideo)
{
this.noVideo = noVideo;
BeatmapSet = set;
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)
{

View File

@ -109,7 +109,7 @@ namespace osu.Game
dependencies.Cache(this);
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;
}

View File

@ -29,8 +29,11 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Container noVideoButtons;
private readonly FillFlowContainer videoButtons;
private readonly AuthorInfo author;
private readonly Container downloadButtonsContainer;
public Details Details;
private BeatmapManager beatmaps;
private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker;
@ -48,6 +51,7 @@ namespace osu.Game.Overlays.BeatmapSet
title.Text = BeatmapSet.Metadata.Title;
artist.Text = BeatmapSet.Metadata.Artist;
downloadButtonsContainer.FadeIn();
noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration);
videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration);
@ -164,7 +168,7 @@ namespace osu.Game.Overlays.BeatmapSet
Children = new Drawable[]
{
new FavouriteButton(),
new Container
downloadButtonsContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
@ -174,7 +178,10 @@ namespace osu.Game.Overlays.BeatmapSet
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
Child = new DownloadButton("Download", @""),
Child = new DownloadButton("Download", @"")
{
Action = () => download(false),
},
},
videoButtons = new FillFlowContainer
{
@ -183,8 +190,14 @@ namespace osu.Game.Overlays.BeatmapSet
Alpha = 0f,
Children = new[]
{
new DownloadButton("Download", "with Video"),
new DownloadButton("Download", "without Video"),
new DownloadButton("Download", "with Video")
{
Action = () => download(false),
},
new DownloadButton("Download", "without Video")
{
Action = () => download(true),
},
},
},
},
@ -208,9 +221,39 @@ namespace osu.Game.Overlays.BeatmapSet
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OsuColour colours, BeatmapManager beatmaps)
{
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 tabBackground;
public Bindable<double> ChatHeight { get; internal set; }
public Bindable<double> ChatHeight { get; set; }
private readonly Container channelSelectionContainer;
private readonly ChannelSelectionOverlay channelSelection;

View File

@ -16,9 +16,7 @@ using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Framework.Logging;
using osu.Game.Overlays.Notifications;
using osu.Game.Online.API.Requests;
using osu.Framework.Configuration;
using osu.Framework.Audio.Track;
@ -35,10 +33,8 @@ namespace osu.Game.Overlays.Direct
private Container content;
private APIAccess api;
private ProgressBar progressBar;
private BeatmapManager beatmaps;
private NotificationOverlay notifications;
private BeatmapSetOverlay beatmapSetOverlay;
public Track Preview => PlayButton.Preview;
@ -71,11 +67,9 @@ namespace osu.Game.Overlays.Direct
[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.notifications = notifications;
this.beatmapSetOverlay = beatmapSetOverlay;
AddInternal(content = new Container
@ -109,6 +103,14 @@ namespace osu.Game.Overlays.Direct
if (downloadRequest != null)
attachDownload(downloadRequest);
beatmaps.BeatmapDownloadBegan += attachDownload;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
beatmaps.BeatmapDownloadBegan -= attachDownload;
}
protected override void Update()
@ -151,16 +153,6 @@ namespace osu.Game.Overlays.Direct
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)
{
// we already have an active download running.
@ -172,13 +164,14 @@ namespace osu.Game.Overlays.Direct
return;
}
var request = beatmaps.Download(SetInfo);
attachDownload(request);
beatmaps.Download(SetInfo);
}
private void attachDownload(DownloadBeatmapSetRequest request)
{
if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID)
return;
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);

View File

@ -19,7 +19,7 @@ using OpenTK.Input;
namespace osu.Game.Overlays.KeyBinding
{
internal class KeyBindingRow : Container, IFilterable
public class KeyBindingRow : Container, IFilterable
{
private readonly object action;
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]
private void load(OsuColour colours)

View File

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

View File

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

View File

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

View File

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

View File

@ -37,7 +37,7 @@ namespace osu.Game.Overlays
private const float bottom_black_area_height = 55;
private Drawable currentBackground;
private Drawable background;
private ProgressBar progressBar;
private IconButton prevButton;
@ -120,7 +120,7 @@ namespace osu.Game.Overlays
},
Children = new[]
{
currentBackground = new Background(),
background = new Background(),
title = new OsuSpriteText
{
Origin = Anchor.BottomCentre,
@ -334,6 +334,7 @@ namespace osu.Game.Overlays
pendingBeatmapSwitch = Schedule(delegate
{
// todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
Task.Run(() =>
{
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);
newBackground.MoveToX(0, 500, Easing.OutCubic);
currentBackground.MoveToX(-400, 500, Easing.OutCubic);
break;
case TransformDirection.Prev:
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
currentBackground.MoveToX(400, 500, Easing.OutCubic);
break;
}
currentBackground.Expire();
currentBackground = newBackground;
case TransformDirection.Next:
newBackground.Position = new Vector2(400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(-400, 500, Easing.OutCubic);
break;
case TransformDirection.Prev:
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(400, 500, Easing.OutCubic);
break;
}
})
{
Depth = float.MaxValue,
background.Expire();
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);
ShowMoreLoading.Hide();
if (!sets.Any())
if (!sets.Any() && VisiblePages == 1)
{
MissingText.Show();
return;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ using osu.Game.Graphics;
namespace osu.Game.Overlays.Toolbar
{
internal class ToolbarHomeButton : ToolbarButton
public class ToolbarHomeButton : ToolbarButton
{
public ToolbarHomeButton()
{
@ -14,4 +14,4 @@ namespace osu.Game.Overlays.Toolbar
TooltipSub = "Return to the main menu";
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Judgements
/// <summary>
/// The combo prior to this judgement occurring.
/// </summary>
internal int ComboAtJudgement;
public int ComboAtJudgement;
/// <summary>
/// The highest combo achieved prior to this judgement occurring.
/// </summary>
internal int HighestComboAtJudgement;
public int HighestComboAtJudgement;
/// <summary>
/// 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.
/// Populated when added via <see cref="DrawableHitObject{TObject}.AddJudgement"/>.
/// </summary>
public double TimeOffset { get; internal set; }
public double TimeOffset { get; set; }
/// <summary>
/// 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>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mods
{
public abstract class ModPerfect : ModSuddenDeath
@ -8,5 +10,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Perfect";
public override string ShortenedName => "PF";
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 osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
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 ShortenedName => "SD";
@ -16,5 +17,12 @@ namespace osu.Game.Rulesets.Mods
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
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>
/// A HitObjectParser to parse legacy osu!catch Beatmaps.
/// </summary>
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
protected override HitObject CreateHit(Vector2 position, bool newCombo)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.UI
/// </summary>
public HitObjectContainer HitObjects { get; protected set; }
internal Container<Drawable> ScaledContent;
public Container<Drawable> ScaledContent;
/// <summary>
/// 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"/>.
/// </summary>
/// <param name="ruleset">The ruleset being repesented.</param>
internal RulesetContainer(Ruleset ruleset)
protected RulesetContainer(Ruleset ruleset)
{
Ruleset = ruleset;
}

View File

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

View File

@ -3,7 +3,7 @@
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
{
internal class ChartListing : ScreenWhiteBox
public class ChartListing : ScreenWhiteBox
{
protected override IEnumerable<Type> PossibleChildren => new[] {
typeof(ChartInfo)

View File

@ -3,7 +3,7 @@
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>
/// The part of the timeline that displays bookmarks.
/// </summary>
internal class BookmarkPart : TimelinePart
public class BookmarkPart : TimelinePart
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{

View File

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

View File

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

View File

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

View File

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

View File

@ -3,11 +3,9 @@
using OpenTK;
using osu.Framework.Allocation;
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;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
@ -16,83 +14,64 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
/// <summary>
/// The timeline that sits at the bottom of the editor.
/// </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;
public SummaryTimeline()
{
Masking = true;
CornerRadius = corner_radius;
TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart;
InternalChildren = new[]
Children = new[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new Container
markerPart = new MarkerPart { RelativeSizeAxes = Axes.Both },
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,
Padding = new MarginPadding { Left = contents_padding, Right = contents_padding },
Children = new[]
Children = new Drawable[]
{
markerPart = new MarkerPart { RelativeSizeAxes = Axes.Both },
controlPointPart = new ControlPointPart
new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Size = new Vector2(5)
},
bookmarkPart = new BookmarkPart
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
EdgeSmoothness = new Vector2(0, 1),
},
timelineBar = new Container
new Circle
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
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)
},
}
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]
private void load(OsuColour colours)
{
background.Colour = colours.Gray1;
timelineBar.Colour = colours.Gray5;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -17,6 +17,7 @@ using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio;
using osu.Framework.Threading;
namespace osu.Game.Screens.Menu
{
@ -38,10 +39,10 @@ namespace osu.Game.Screens.Menu
private readonly FlowContainerWithOrigin buttonFlow;
//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;
internal const float WEDGE_WIDTH = 20;
public const float BUTTON_WIDTH = 140f;
public const float WEDGE_WIDTH = 20;
private OsuLogo logo;
@ -55,6 +56,8 @@ namespace osu.Game.Screens.Menu
// osuLogo.SizeForFlow relies on loading to be complete.
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)
buttonArea.FinishTransforms(true);
updateLogoState(lastState);
using (buttonArea.BeginDelayedSequence(lastState == MenuState.Initial ? 150 : 0, true))
{
switch (state)
{
case MenuState.Exit:
case MenuState.Initial:
trackingPosition = false;
buttonAreaBackground.ScaleTo(Vector2.One, 500, Easing.Out);
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)
b.State = ButtonState.Contracted;
@ -252,33 +243,6 @@ namespace osu.Game.Screens.Menu
case MenuState.TopLevel:
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);
foreach (Button b in buttonsTopLevel)
@ -297,8 +261,6 @@ namespace osu.Game.Screens.Menu
case MenuState.EnteringMode:
buttonAreaBackground.ScaleTo(new Vector2(2, 0), 300, Easing.InSine);
trackingPosition = true;
buttonsTopLevel.ForEach(b => b.ContractStyle = 1);
buttonsPlay.ForEach(b => b.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()
{
@ -333,8 +355,8 @@ namespace osu.Game.Screens.Menu
if (logo != null)
{
if (trackingPosition)
logo.Position = iconTrackingPosition;
if (logoTracking)
logo.Position = logoTrackingPosition;
iconFacade.Width = logo.SizeForFlow * 0.5f;
}

View File

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

View File

@ -85,12 +85,11 @@ namespace osu.Game.Screens.Menu
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];
var effect = beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current);
for (int i = 0; i < bars_per_visualiser; i++)
{
if (track?.IsRunning ?? false)

View File

@ -226,7 +226,7 @@ namespace osu.Game.Screens.Menu
/// </summary>
/// <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>
internal void AppendAnimatingAction(Action action, bool waitForPrevious)
public void AppendAnimatingAction(Action action, bool waitForPrevious)
{
Action runnableAction = () =>
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Ranking
{
internal class ResultsPageScore : ResultsPage
public class ResultsPageScore : ResultsPage
{
private ScoreCounter scoreCounter;
@ -324,7 +324,7 @@ namespace osu.Game.Screens.Ranking
title.Colour = artist.Colour = colours.BlueDarker;
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);
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
{
internal class BeatmapCarousel : OsuScrollContainer
public class BeatmapCarousel : OsuScrollContainer
{
public BeatmapInfo SelectedBeatmap => selectedPanel?.Beatmap;
@ -116,7 +116,7 @@ namespace osu.Game.Screens.Select
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.
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,
// and depth flows downward.
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
{
internal class BeatmapInfoWedge : OverlayContainer
public class BeatmapInfoWedge : OverlayContainer
{
private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0);
private Drawable beatmapInfoContainer;
private Drawable info;
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 void PopIn()
@ -65,56 +59,62 @@ namespace osu.Game.Screens.Select
public void UpdateBeatmap(WorkingBeatmap beatmap)
{
var lastContainer = beatmapInfoContainer;
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();
}
})
LoadComponentAsync(new BufferedWedgeInfo(beatmap)
{
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 BufferedWedgeInfo(WorkingBeatmap beatmap)
private readonly WorkingBeatmap working;
public BufferedWedgeInfo(WorkingBeatmap working)
{
BeatmapInfo beatmapInfo = beatmap.BeatmapInfo;
BeatmapMetadata metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
this.working = working;
}
[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>();
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;
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "Length",
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
{
Name = "BPM",
Icon = FontAwesome.fa_circle,
Content = getBPMRange(beatmap.Beatmap),
Content = getBPMRange(beatmap),
}));
//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;
@ -140,7 +140,7 @@ namespace osu.Game.Screens.Select
Children = new[]
{
// Zoomed-in and cropped beatmap background
new BeatmapBackgroundSprite(beatmap)
new BeatmapBackgroundSprite(working)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
@ -149,7 +149,7 @@ namespace osu.Game.Screens.Select
},
},
},
new DifficultyColourBar(beatmap.BeatmapInfo)
new DifficultyColourBar(beatmapInfo)
{
RelativeSizeAxes = Axes.Y,
Width = 20,
@ -206,13 +206,13 @@ namespace osu.Game.Screens.Select
Font = @"Exo2.0-Medium",
Text = "mapped by ",
TextSize = 15,
},
},
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = metadata.Author.Username,
TextSize = 15,
},
},
}
},
new FillFlowContainer
@ -244,38 +244,39 @@ namespace osu.Game.Screens.Select
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new SpriteIcon
{
Icon = FontAwesome.fa_square,
Origin = Anchor.Centre,
Colour = new Color4(68, 17, 136, 255),
Rotation = 45,
Size = new Vector2(20),
},
new SpriteIcon
{
Icon = statistic.Icon,
Origin = Anchor.Centre,
Colour = new Color4(255, 221, 85, 255),
Scale = new Vector2(0.8f),
Size = new Vector2(20),
},
new OsuSpriteText
{
Margin = new MarginPadding { Left = 13 },
Font = @"Exo2.0-Bold",
Colour = new Color4(255, 221, 85, 255),
Text = statistic.Content,
TextSize = 17,
Origin = Anchor.CentreLeft
},
new SpriteIcon
{
Icon = FontAwesome.fa_square,
Origin = Anchor.Centre,
Colour = new Color4(68, 17, 136, 255),
Rotation = 45,
Size = new Vector2(20),
},
new SpriteIcon
{
Icon = statistic.Icon,
Origin = Anchor.Centre,
Colour = new Color4(255, 221, 85, 255),
Scale = new Vector2(0.8f),
Size = new Vector2(20),
},
new OsuSpriteText
{
Margin = new MarginPadding { Left = 13 },
Font = @"Exo2.0-Bold",
Colour = new Color4(255, 221, 85, 255),
Text = statistic.Content,
TextSize = 17,
Origin = Anchor.CentreLeft
},
};
}
}
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[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = AccentColour,
Width = full_opacity_ratio,
},
new Box
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = AccentColour,
Alpha = 0.5f,
X = full_opacity_ratio,
Width = 1 - full_opacity_ratio,
}
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = AccentColour,
Width = full_opacity_ratio,
},
new Box
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = AccentColour,
Alpha = 0.5f,
X = full_opacity_ratio,
Width = 1 - full_opacity_ratio,
}
};
}
}

View File

@ -40,20 +40,12 @@ namespace osu.Game.Screens.Select.Leaderboards
scores = value;
getScoresRequest?.Cancel();
int i = 150;
scrollFlow?.FadeOut(200);
scrollFlow?.Expire();
scrollFlow = 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;
}
// schedule because we may not be loaded yet (LoadComponentAsync complains).
Schedule(() =>
@ -64,13 +56,12 @@ namespace osu.Game.Screens.Select.Leaderboards
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 5f),
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 =>
{
scrollFlow?.Expire();
scrollContainer.Add(scrollFlow = f);
i = 0;
int i = 0;
foreach (var s in f.Children)
{
using (s.BeginDelayedSequence(i++ * 50, true))

View File

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

View File

@ -12,7 +12,7 @@ using System.Linq;
namespace osu.Game.Screens.Tournament.Components
{
internal class VisualiserContainer : Container
public class VisualiserContainer : Container
{
/// <summary>
/// Number of lines in the visualiser.

View File

@ -16,6 +16,7 @@ using osu.Game.Overlays;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Backgrounds;
namespace osu.Game.Users
{
@ -43,6 +44,8 @@ namespace osu.Game.Users
this.user = user;
FillFlowContainer infoContainer;
Height = height - status_height;
Masking = true;
CornerRadius = 5;
@ -100,7 +103,7 @@ namespace osu.Game.Users
TextSize = 18,
Font = @"Exo2.0-SemiBoldItalic",
},
new FillFlowContainer
infoContainer = new FillFlowContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
@ -115,16 +118,6 @@ namespace osu.Game.Users
Width = 30f,
RelativeSizeAxes = Axes.Y,
},
new Container
{
Width = 40f,
RelativeSizeAxes = Axes.Y,
},
new CircularContainer
{
Width = 20f,
RelativeSizeAxes = Axes.Y,
},
},
},
},
@ -171,6 +164,13 @@ namespace osu.Game.Users
},
},
};
if (user.IsSupporter)
infoContainer.Add(new SupporterIcon
{
RelativeSizeAxes = Axes.Y,
Width = 20f,
});
}
[BackgroundDependencyLoader(permitNulls: true)]
@ -219,5 +219,53 @@ namespace osu.Game.Users
{
new OsuMenuItem("View Profile", MenuItemType.Highlighted, ViewProfile),
};
private class SupporterIcon : CircularContainer
{
private readonly Box background;
public SupporterIcon()
{
Masking = true;
Children = new Drawable[]
{
new Box { RelativeSizeAxes = Axes.Both },
new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.8f),
Masking = true,
Children = new Drawable[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new Triangles
{
TriangleScale = 0.2f,
ColourLight = OsuColour.FromHex(@"ff7db7"),
ColourDark = OsuColour.FromHex(@"de5b95"),
RelativeSizeAxes = Axes.Both,
Velocity = 0.3f,
},
}
},
new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.fa_heart,
Scale = new Vector2(0.45f),
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Pink;
}
}
}
}

View File

@ -279,6 +279,10 @@
<Compile Include="Migrations\20171025071459_AddMissingIndexRules.Designer.cs">
<DependentUpon>20171025071459_AddMissingIndexRules.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs" />
<Compile Include="Migrations\20171119065731_AddBeatmapOnlineIDUniqueConstraint.designer.cs">
<DependentUpon>20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\OsuDbContextModelSnapshot.cs" />
<Compile Include="Online\API\Requests\GetBeatmapSetRequest.cs" />
<Compile Include="Online\API\Requests\GetBeatmapSetsResponse.cs" />
@ -295,6 +299,10 @@
<Compile Include="Overlays\Profile\Sections\Ranks\DrawableTotalScore.cs" />
<Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" />
<Compile Include="Overlays\Settings\SettingsButton.cs" />
<Compile Include="Screens\Edit\Components\BottomBarContainer.cs" />
<Compile Include="Screens\Edit\Components\PlaybackControl.cs" />
<Compile Include="Screens\Edit\Components\TimeInfoContainer.cs" />
<Compile Include="Rulesets\Mods\IApplicableToScoreProcessor.cs" />
<Compile Include="Screens\Edit\Screens\Compose\Timeline\BeatmapWaveformGraph.cs" />
<Compile Include="Beatmaps\Drawables\DifficultyColouredContainer.cs" />
<Compile Include="Beatmaps\Drawables\DifficultyIcon.cs" />