1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 06:35:05 +08:00

Merge branch 'master' into user-panels

This commit is contained in:
Dean Herbert 2017-11-21 16:41:48 +09:00 committed by GitHub
commit 8452446803
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 768 additions and 428 deletions

@ -1 +1 @@
Subproject commit c95b9350edb6305cfefdf08f902f6f73d336736b Subproject commit 14eb531c0056b8569f21b3571890383ffbea768e

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

@ -14,11 +14,8 @@ namespace osu.Game.Rulesets.Catch
{ {
} }
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty) public override double Calculate(Dictionary<string, string> categoryDifficulty = null) => 0;
{
return 0;
}
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter(); protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
} }
} }

View File

@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
public override int LegacyID => 2; public override int LegacyID => 2;

View File

@ -16,11 +16,8 @@ namespace osu.Game.Rulesets.Mania
{ {
} }
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty) public override double Calculate(Dictionary<string, string> categoryDifficulty = null) => 0;
{
return 0;
}
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize))); protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize)));
} }
} }

View File

@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Mania
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap);
public override int LegacyID => 3; public override int LegacyID => 3;

View File

@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
} }
public class ManiaModRandom : Mod, IApplicableMod<ManiaHitObject> public class ManiaModRandom : Mod, IApplicableToRulesetContainer<ManiaHitObject>
{ {
public override string Name => "Random"; public override string Name => "Random";
public override string ShortenedName => "RD"; public override string ShortenedName => "RD";
@ -97,7 +97,6 @@ namespace osu.Game.Rulesets.Mania.Mods
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer) public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{ {
int availableColumns = ((ManiaRulesetContainer)rulesetContainer).AvailableColumns; int availableColumns = ((ManiaRulesetContainer)rulesetContainer).AvailableColumns;
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList(); var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]); rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
@ -188,6 +187,7 @@ namespace osu.Game.Rulesets.Mania.Mods
base.ApplyToRulesetContainer(rulesetContainer); base.ApplyToRulesetContainer(rulesetContainer);
} }
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap) => new Score protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap) => new Score
{ {
User = new User { Username = "osu!topus!" }, User = new User { Username = "osu!topus!" },

View File

@ -9,7 +9,6 @@ using osu.Game.Rulesets.Osu.Objects;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
@ -33,22 +32,29 @@ namespace osu.Game.Rulesets.Osu.Mods
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
} }
public class OsuModHardRock : ModHardRock, IApplicableMod<OsuHitObject> public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject>
{ {
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
public override bool Ranked => true; public override bool Ranked => true;
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer) public void ApplyToHitObject(OsuHitObject hitObject)
{
hitObject.Position = new Vector2(hitObject.Position.X, OsuPlayfield.BASE_SIZE.Y - hitObject.Y);
var slider = hitObject as Slider;
if (slider == null)
return;
var newControlPoints = new List<Vector2>();
slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
slider.ControlPoints = newControlPoints;
slider.Curve?.Calculate(); // Recalculate the slider curve
}
public void ApplyToHitObjects(RulesetContainer<OsuHitObject> rulesetContainer)
{ {
rulesetContainer.Objects.OfType<OsuHitObject>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Y));
rulesetContainer.Objects.OfType<Slider>().ForEach(s =>
{
var newControlPoints = new List<Vector2>();
s.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
s.ControlPoints = newControlPoints;
s.Curve?.Calculate(); // Recalculate the slider curve
});
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
@ -16,19 +17,25 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
private const int section_length = 400; private const int section_length = 400;
private const double difficulty_multiplier = 0.0675; private const double difficulty_multiplier = 0.0675;
public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap) public OsuDifficultyCalculator(Beatmap beatmap)
: base(beatmap)
{
}
public OsuDifficultyCalculator(Beatmap beatmap, Mod[] mods)
: base(beatmap, mods)
{ {
} }
protected override void PreprocessHitObjects() protected override void PreprocessHitObjects()
{ {
foreach (OsuHitObject h in Objects) foreach (OsuHitObject h in Beatmap.HitObjects)
(h as Slider)?.Curve?.Calculate(); (h as Slider)?.Curve?.Calculate();
} }
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty) public override double Calculate(Dictionary<string, string> categoryDifficulty = null)
{ {
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Objects); OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects);
Skill[] skills = Skill[] skills =
{ {
new Aim(), new Aim(),
@ -67,6 +74,6 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
return starRating; return starRating;
} }
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter(); protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter(Beatmap beatmap) => new OsuBeatmapConverter();
} }
} }

View File

@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods);
public override string Description => "osu!"; public override string Description => "osu!";

View File

@ -36,12 +36,12 @@ namespace osu.Game.Rulesets.Taiko
{ {
} }
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty) public override double Calculate(Dictionary<string, string> categoryDifficulty = null)
{ {
// Fill our custom DifficultyHitObject class, that carries additional information // Fill our custom DifficultyHitObject class, that carries additional information
difficultyHitObjects.Clear(); difficultyHitObjects.Clear();
foreach (var hitObject in Objects) foreach (var hitObject in Beatmap.HitObjects)
difficultyHitObjects.Add(new TaikoHitObjectDifficulty(hitObject)); difficultyHitObjects.Add(new TaikoHitObjectDifficulty(hitObject));
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
@ -134,6 +134,6 @@ namespace osu.Game.Rulesets.Taiko
return difficulty; return difficulty;
} }
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(true); protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter(Beatmap beatmap) => new TaikoBeatmapConverter(true);
} }
} }

View File

@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap);
public override int LegacyID => 1; public override int LegacyID => 1;

View File

@ -483,7 +483,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,6 +511,10 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID);
if (existing == null)
{
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary // TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null; beatmap.BeatmapInfo.Metadata = null;
@ -522,6 +527,7 @@ namespace osu.Game.Beatmaps
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
} }
} }
}
return beatmapSet; return beatmapSet;
} }

View File

@ -3,6 +3,10 @@
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Rulesets.Mods;
using osu.Framework.Timing;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
@ -10,45 +14,46 @@ namespace osu.Game.Beatmaps
{ {
protected double TimeRate = 1; protected double TimeRate = 1;
protected abstract double CalculateInternal(Dictionary<string, string> categoryDifficulty); public abstract double Calculate(Dictionary<string, string> categoryDifficulty = null);
private void loadTiming()
{
// TODO: Handle mods
const int audio_rate = 100;
TimeRate = audio_rate / 100.0;
}
public double Calculate(Dictionary<string, string> categoryDifficulty = null)
{
loadTiming();
double difficulty = CalculateInternal(categoryDifficulty);
return difficulty;
}
} }
public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject
{ {
protected readonly Beatmap Beatmap; protected readonly Beatmap<T> Beatmap;
protected readonly Mod[] Mods;
protected List<T> Objects; protected DifficultyCalculator(Beatmap beatmap, Mod[] mods = null)
protected DifficultyCalculator(Beatmap beatmap)
{ {
Beatmap = beatmap; Beatmap = CreateBeatmapConverter(beatmap).Convert(beatmap);
Mods = mods ?? new Mod[0];
Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects;
foreach (var h in Objects) ApplyMods(Mods);
h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
PreprocessHitObjects(); PreprocessHitObjects();
} }
protected virtual void ApplyMods(Mod[] mods)
{
var clock = new StopwatchClock();
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
TimeRate = clock.Rate;
foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty);
foreach (var mod in mods.OfType<IApplicableToHitObject<T>>())
foreach (var obj in Beatmap.HitObjects)
mod.ApplyToHitObject(obj);
foreach (var h in Beatmap.HitObjects)
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
}
protected virtual void PreprocessHitObjects() protected virtual void PreprocessHitObjects()
{ {
} }
protected abstract BeatmapConverter<T> CreateBeatmapConverter(); protected abstract BeatmapConverter<T> CreateBeatmapConverter(Beatmap beatmap);
} }
} }

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,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;
@ -59,7 +59,7 @@ namespace osu.Game.Beatmaps
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => null; public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => null;
public override string Description => "dummy"; public override string Description => "dummy";

View File

@ -28,16 +28,11 @@ namespace osu.Game.Beatmaps
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
Mods.ValueChanged += mods => applyRateAdjustments(); Mods.ValueChanged += mods => applyRateAdjustments();
}
private void applyRateAdjustments() beatmap = new Lazy<Beatmap>(populateBeatmap);
{ background = new Lazy<Texture>(populateBackground);
var t = track; track = new Lazy<Track>(populateTrack);
if (t == null) return; waveform = new Lazy<Waveform>(populateWaveform);
t.ResetSpeedAdjustments();
foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(t);
} }
protected abstract Beatmap GetBeatmap(); protected abstract Beatmap GetBeatmap();
@ -45,98 +40,72 @@ namespace osu.Game.Beatmaps
protected abstract Track GetTrack(); protected abstract Track GetTrack();
protected virtual Waveform GetWaveform() => new Waveform(); protected virtual Waveform GetWaveform() => new Waveform();
private Beatmap beatmap; public bool BeatmapLoaded => beatmap.IsValueCreated;
private readonly object beatmapLock = new object(); public Beatmap Beatmap => beatmap.Value;
public Beatmap Beatmap private readonly Lazy<Beatmap> beatmap;
{
get
{
lock (beatmapLock)
{
if (beatmap != null) return beatmap;
beatmap = GetBeatmap() ?? new Beatmap(); private Beatmap populateBeatmap()
{
var b = GetBeatmap() ?? new Beatmap();
// use the database-backed info. // use the database-backed info.
beatmap.BeatmapInfo = BeatmapInfo; b.BeatmapInfo = BeatmapInfo;
return beatmap; return b;
}
}
} }
private readonly object backgroundLock = new object(); public bool BackgroundLoaded => background.IsValueCreated;
private Texture background; public Texture Background => background.Value;
public Texture Background private Lazy<Texture> background;
{
get
{
lock (backgroundLock)
{
return background ?? (background = GetBackground());
}
}
}
private Track track; private Texture populateBackground() => GetBackground();
private readonly object trackLock = new object();
public Track Track
{
get
{
lock (trackLock)
{
if (track != null) return track;
public bool TrackLoaded => track.IsValueCreated;
public Track Track => track.Value;
private Lazy<Track> track;
private Track populateTrack()
{
// we want to ensure that we always have a track, even if it's a fake one. // we want to ensure that we always have a track, even if it's a fake one.
track = GetTrack() ?? new TrackVirtual(); var t = GetTrack() ?? new TrackVirtual();
applyRateAdjustments(t);
applyRateAdjustments(); return t;
return track;
}
}
} }
private Waveform waveform; public bool WaveformLoaded => waveform.IsValueCreated;
private readonly object waveformLock = new object(); public Waveform Waveform => waveform.Value;
public Waveform Waveform private readonly Lazy<Waveform> waveform;
{
get
{
lock (waveformLock)
return waveform ?? (waveform = GetWaveform());
}
}
public bool TrackLoaded => track != null; private Waveform populateWaveform() => GetWaveform();
public void TransferTo(WorkingBeatmap other) public void TransferTo(WorkingBeatmap other)
{ {
lock (trackLock) if (track.IsValueCreated && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
{
if (track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
other.track = track; other.track = track;
}
if (background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) if (background.IsValueCreated && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo))
other.background = background; other.background = background;
} }
public virtual void Dispose() public virtual void Dispose()
{ {
background?.Dispose(); if (BackgroundLoaded) Background?.Dispose();
background = null; if (WaveformLoaded) Waveform?.Dispose();
waveform?.Dispose();
} }
public void DisposeTrack() public void DisposeTrack()
{ {
lock (trackLock) if (TrackLoaded) Track?.Dispose();
{
track?.Dispose();
track = null;
} }
private void applyRateAdjustments(Track t = null)
{
if (t == null && track.IsValueCreated) t = Track;
if (t == null) return;
t.ResetSpeedAdjustments();
foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(t);
} }
} }
} }

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

@ -35,9 +35,12 @@ namespace osu.Game.Graphics.Containers
protected override void Update() protected override void Update()
{ {
var track = Beatmap.Value.Track; if (!Beatmap.Value.TrackLoaded || !Beatmap.Value.BeatmapLoaded) return;
if (track == null) var track = Beatmap.Value.Track;
var beatmap = Beatmap.Value.Beatmap;
if (track == null || beatmap == null)
return; return;
double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime; double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime;

View File

@ -7,7 +7,7 @@ using OpenTK.Input;
namespace osu.Game.Graphics.Containers namespace osu.Game.Graphics.Containers
{ {
internal class OsuScrollContainer : ScrollContainer public class OsuScrollContainer : ScrollContainer
{ {
/// <summary> /// <summary>
/// Allows controlling the scroll bar from any position in the container using the right mouse button. /// Allows controlling the scroll bar from any position in the container using the right mouse button.

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

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

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

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

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

@ -1,22 +0,0 @@
// 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.Objects;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for mods that are applied to a RulesetContainer.
/// </summary>
/// <typeparam name="TObject">The type of HitObject the RulesetContainer contains.</typeparam>
public interface IApplicableMod<TObject>
where TObject : HitObject
{
/// <summary>
/// Applies the mod to a RulesetContainer.
/// </summary>
/// <param name="rulesetContainer">The RulesetContainer to apply the mod to.</param>
void ApplyToRulesetContainer(RulesetContainer<TObject> rulesetContainer);
}
}

View File

@ -0,0 +1,20 @@
// 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.Objects;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for <see cref="Mod"/>s that can be applied to <see cref="HitObject"/>s.
/// </summary>
public interface IApplicableToHitObject<in TObject>
where TObject : HitObject
{
/// <summary>
/// Applies this <see cref="IApplicableToHitObject{TObject}"/> to a <see cref="HitObject"/>.
/// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> to apply to.</param>
void ApplyToHitObject(TObject hitObject);
}
}

View File

@ -0,0 +1,21 @@
// 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.Objects;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for <see cref="Mod"/>s that can be applied to <see cref="RulesetContainer"/>s.
/// </summary>
public interface IApplicableToRulesetContainer<TObject>
where TObject : HitObject
{
/// <summary>
/// Applies this <see cref="IApplicableToRulesetContainer{TObject}"/> to a <see cref="RulesetContainer{TObject}"/>.
/// </summary>
/// <param name="rulesetContainer">The <see cref="RulesetContainer{TObject}"/> to apply to.</param>
void ApplyToRulesetContainer(RulesetContainer<TObject> rulesetContainer);
}
}

View File

@ -10,7 +10,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
public abstract class ModAutoplay<T> : ModAutoplay, IApplicableMod<T> public abstract class ModAutoplay<T> : ModAutoplay, IApplicableToRulesetContainer<T>
where T : HitObject where T : HitObject
{ {
protected abstract Score CreateReplayScore(Beatmap<T> beatmap); protected abstract Score CreateReplayScore(Beatmap<T> beatmap);

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

@ -47,7 +47,7 @@ namespace osu.Game.Rulesets
/// <returns></returns> /// <returns></returns>
public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset); public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset);
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap); public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null);
public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle }; public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle };

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;
} }
@ -212,7 +212,11 @@ namespace osu.Game.Rulesets.UI
if (mods == null) if (mods == null)
return; return;
foreach (var mod in mods.OfType<IApplicableMod<TObject>>()) foreach (var mod in mods.OfType<IApplicableToHitObject<TObject>>())
foreach (var obj in Beatmap.HitObjects)
mod.ApplyToHitObject(obj);
foreach (var mod in mods.OfType<IApplicableToRulesetContainer<TObject>>())
mod.ApplyToRulesetContainer(this); mod.ApplyToRulesetContainer(this);
} }

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

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

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

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

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

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

View File

@ -22,7 +22,7 @@ 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);

View File

@ -17,19 +17,21 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using System.Linq;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Screens.Select.Leaderboards
{ {
public class Leaderboard : Container public class Leaderboard : Container
{ {
private readonly ScrollContainer scrollContainer; private readonly ScrollContainer scrollContainer;
private readonly FillFlowContainer<LeaderboardScore> scrollFlow; private FillFlowContainer<LeaderboardScore> scrollFlow;
public Action<Score> ScoreSelected; public Action<Score> ScoreSelected;
private readonly LoadingAnimation loading; private readonly LoadingAnimation loading;
private IEnumerable<Score> scores; private IEnumerable<Score> scores;
public IEnumerable<Score> Scores public IEnumerable<Score> Scores
{ {
get { return scores; } get { return scores; }
@ -38,36 +40,37 @@ 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)
{
foreach (var c in scrollFlow.Children)
c.FadeOut(i += 10);
foreach (var c in scrollFlow.Children)
c.LifetimeEnd = Time.Current + i;
return; return;
}
scrollFlow.Clear(); // schedule because we may not be loaded yet (LoadComponentAsync complains).
Schedule(() =>
i = 0;
foreach (var s in scores)
{ {
var ls = new LeaderboardScore(s, 1 + i) LoadComponentAsync(new FillFlowContainer<LeaderboardScore>
{ {
AlwaysPresent = true, RelativeSizeAxes = Axes.X,
Action = () => ScoreSelected?.Invoke(s), AutoSizeAxes = Axes.Y,
State = Visibility.Hidden, Spacing = new Vector2(0f, 5f),
}; Padding = new MarginPadding { Top = 10, Bottom = 5 },
scrollFlow.Add(ls); ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
}, f =>
{
scrollContainer.Add(scrollFlow = f);
using (BeginDelayedSequence(i++ * 50, true)) int i = 0;
ls.Show(); foreach (var s in f.Children)
{
using (s.BeginDelayedSequence(i++ * 50, true))
s.Show();
} }
scrollContainer.ScrollTo(0f, false); scrollContainer.ScrollTo(0f, false);
});
});
} }
} }
@ -79,16 +82,6 @@ namespace osu.Game.Screens.Select.Leaderboards
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false, ScrollbarVisible = false,
Children = new Drawable[]
{
scrollFlow = new FillFlowContainer<LeaderboardScore>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 5f),
Padding = new MarginPadding { Top = 10, Bottom = 5 },
},
},
}, },
loading = new LoadingAnimation() loading = new LoadingAnimation()
}; };
@ -152,6 +145,8 @@ namespace osu.Game.Screens.Select.Leaderboards
if (!scrollContainer.IsScrolledToEnd()) if (!scrollContainer.IsScrolledToEnd())
fadeStart -= LeaderboardScore.HEIGHT; fadeStart -= LeaderboardScore.HEIGHT;
if (scrollFlow == null) return;
foreach (var c in scrollFlow.Children) foreach (var c in scrollFlow.Children)
{ {
var topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scrollFlow).Y; var topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scrollFlow).Y;

View File

@ -2,9 +2,10 @@
// 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;
using System.Linq;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -13,14 +14,13 @@ using osu.Framework.Input;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Screens.Select.Leaderboards namespace osu.Game.Screens.Select.Leaderboards
{ {
public class LeaderboardScore : OsuClickableContainer, IStateful<Visibility> public class LeaderboardScore : OsuClickableContainer
{ {
public static readonly float HEIGHT = 60; public static readonly float HEIGHT = 60;
@ -34,72 +34,16 @@ namespace osu.Game.Screens.Select.Leaderboards
private const float background_alpha = 0.25f; private const float background_alpha = 0.25f;
private const float rank_width = 30; private const float rank_width = 30;
private readonly Box background; private Box background;
private readonly Container content; private Container content;
private readonly Container avatar; private Container avatar;
private readonly DrawableRank scoreRank; private DrawableRank scoreRank;
private readonly OsuSpriteText nameLabel; private OsuSpriteText nameLabel;
private readonly GlowingSpriteText scoreLabel; private GlowingSpriteText scoreLabel;
private readonly ScoreComponentLabel maxCombo; private ScoreComponentLabel maxCombo;
private readonly ScoreComponentLabel accuracy; private ScoreComponentLabel accuracy;
private readonly Container flagBadgeContainer; private Container flagBadgeContainer;
private readonly FillFlowContainer<ModIcon> modsContainer; private FillFlowContainer<ModIcon> modsContainer;
private Visibility state;
public Visibility State
{
get { return state; }
set
{
if (state == value)
return;
state = value;
switch (state)
{
case Visibility.Hidden:
foreach (var d in new Drawable[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer })
d.FadeOut();
Alpha = 0;
content.MoveToY(75);
avatar.MoveToX(75);
nameLabel.MoveToX(150);
break;
case Visibility.Visible:
this.FadeIn(200);
content.MoveToY(0, 800, Easing.OutQuint);
using (BeginDelayedSequence(100, true))
{
avatar.FadeIn(300, Easing.OutQuint);
nameLabel.FadeIn(350, Easing.OutQuint);
avatar.MoveToX(0, 300, Easing.OutQuint);
nameLabel.MoveToX(0, 350, Easing.OutQuint);
using (BeginDelayedSequence(250, true))
{
scoreLabel.FadeIn(200);
scoreRank.FadeIn(200);
using (BeginDelayedSequence(50, true))
{
var drawables = new Drawable[] { flagBadgeContainer, maxCombo, accuracy, modsContainer, };
for (int i = 0; i < drawables.Length; i++)
drawables[i].FadeIn(100 + i * 50);
}
}
}
break;
}
StateChanged?.Invoke(State);
}
}
public LeaderboardScore(Score score, int rank) public LeaderboardScore(Score score, int rank)
{ {
@ -108,7 +52,11 @@ namespace osu.Game.Screens.Select.Leaderboards
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = HEIGHT; Height = HEIGHT;
}
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[] Children = new Drawable[]
{ {
new Container new Container
@ -255,23 +203,51 @@ namespace osu.Game.Screens.Select.Leaderboards
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
ChildrenEnumerable = Score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
}, },
}, },
}, },
}, },
}, },
}; };
}
foreach (Mod mod in Score.Mods) public override void Show()
{ {
modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.375f) }); foreach (var d in new Drawable[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer })
d.FadeOut();
Alpha = 0;
content.MoveToY(75);
avatar.MoveToX(75);
nameLabel.MoveToX(150);
this.FadeIn(200);
content.MoveToY(0, 800, Easing.OutQuint);
using (BeginDelayedSequence(100, true))
{
avatar.FadeIn(300, Easing.OutQuint);
nameLabel.FadeIn(350, Easing.OutQuint);
avatar.MoveToX(0, 300, Easing.OutQuint);
nameLabel.MoveToX(0, 350, Easing.OutQuint);
using (BeginDelayedSequence(250, true))
{
scoreLabel.FadeIn(200);
scoreRank.FadeIn(200);
using (BeginDelayedSequence(50, true))
{
var drawables = new Drawable[] { flagBadgeContainer, maxCombo, accuracy, modsContainer, };
for (int i = 0; i < drawables.Length; i++)
drawables[i].FadeIn(100 + i * 50);
}
}
} }
} }
public void ToggleVisibility() => State = State == Visibility.Visible ? Visibility.Hidden : Visibility.Visible;
public override void Hide() => State = Visibility.Hidden;
public override void Show() => State = Visibility.Visible;
protected override bool OnHover(InputState state) protected override bool OnHover(InputState state)
{ {

View File

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

View File

@ -279,6 +279,10 @@
<Compile Include="Migrations\20171025071459_AddMissingIndexRules.Designer.cs"> <Compile Include="Migrations\20171025071459_AddMissingIndexRules.Designer.cs">
<DependentUpon>20171025071459_AddMissingIndexRules.cs</DependentUpon> <DependentUpon>20171025071459_AddMissingIndexRules.cs</DependentUpon>
</Compile> </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="Migrations\OsuDbContextModelSnapshot.cs" />
<Compile Include="Online\API\Requests\GetBeatmapSetRequest.cs" /> <Compile Include="Online\API\Requests\GetBeatmapSetRequest.cs" />
<Compile Include="Online\API\Requests\GetBeatmapSetsResponse.cs" /> <Compile Include="Online\API\Requests\GetBeatmapSetsResponse.cs" />
@ -549,9 +553,10 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Rulesets\Judgements\DrawableJudgement.cs" /> <Compile Include="Rulesets\Judgements\DrawableJudgement.cs" />
<Compile Include="Rulesets\Judgements\Judgement.cs" /> <Compile Include="Rulesets\Judgements\Judgement.cs" />
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
<Compile Include="Rulesets\Mods\IApplicableToClock.cs" /> <Compile Include="Rulesets\Mods\IApplicableToClock.cs" />
<Compile Include="Rulesets\Mods\IApplicableToDifficulty.cs" /> <Compile Include="Rulesets\Mods\IApplicableToDifficulty.cs" />
<Compile Include="Rulesets\Mods\IApplicableToHitObject.cs" />
<Compile Include="Rulesets\Mods\IApplicableToRulesetContainer.cs" />
<Compile Include="Rulesets\Mods\Mod.cs" /> <Compile Include="Rulesets\Mods\Mod.cs" />
<Compile Include="Rulesets\Mods\ModAutoplay.cs" /> <Compile Include="Rulesets\Mods\ModAutoplay.cs" />
<Compile Include="Rulesets\Mods\ModCinema.cs" /> <Compile Include="Rulesets\Mods\ModCinema.cs" />