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

Merge remote-tracking branch 'upstream/master' into storyboard-clock-di

This commit is contained in:
David Zhao 2019-07-11 13:33:35 +09:00
commit 6c0de0b436
36 changed files with 983 additions and 239 deletions

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, "AppDelegate"); UIApplication.Main(args, "GameUIApplication", "AppDelegate");
} }
} }
} }

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mania.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, "AppDelegate"); UIApplication.Main(args, "GameUIApplication", "AppDelegate");
} }
} }
} }

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, "AppDelegate"); UIApplication.Main(args, "GameUIApplication", "AppDelegate");
} }
} }
} }

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, "AppDelegate"); UIApplication.Main(args, "GameUIApplication", "AppDelegate");
} }
} }
} }

View File

@ -9,7 +9,7 @@ namespace osu.Game.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, "AppDelegate"); UIApplication.Main(args, "GameUIApplication", "AppDelegate");
} }
} }
} }

View File

@ -108,6 +108,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
StarDifficulty = 9.99, StarDifficulty = 9.99,
Version = @"TEST", Version = @"TEST",
Length = 456000,
Ruleset = maniaRuleset, Ruleset = maniaRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
@ -118,7 +119,6 @@ namespace osu.Game.Tests.Visual.Online
}, },
OnlineInfo = new BeatmapOnlineInfo OnlineInfo = new BeatmapOnlineInfo
{ {
Length = 456000,
CircleCount = 111, CircleCount = 111,
SliderCount = 12, SliderCount = 12,
PlayCount = 222, PlayCount = 222,
@ -181,6 +181,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
StarDifficulty = 5.67, StarDifficulty = 5.67,
Version = @"ANOTHER TEST", Version = @"ANOTHER TEST",
Length = 123000,
Ruleset = taikoRuleset, Ruleset = taikoRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
@ -191,7 +192,6 @@ namespace osu.Game.Tests.Visual.Online
}, },
OnlineInfo = new BeatmapOnlineInfo OnlineInfo = new BeatmapOnlineInfo
{ {
Length = 123000,
CircleCount = 123, CircleCount = 123,
SliderCount = 45, SliderCount = 45,
PlayCount = 567, PlayCount = 567,

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Online
}); });
channelTabControl.OnRequestLeave += channel => channelTabControl.RemoveChannel(channel); channelTabControl.OnRequestLeave += channel => channelTabControl.RemoveChannel(channel);
channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.NewValue.ToString(); channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.NewValue;
AddStep("Add random private channel", addRandomPrivateChannel); AddStep("Add random private channel", addRandomPrivateChannel);
AddAssert("There is only one channels", () => channelTabControl.Items.Count() == 2); AddAssert("There is only one channels", () => channelTabControl.Items.Count() == 2);

View File

@ -3,19 +3,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Graphics; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Overlays.BeatmapSet.Scores;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Users; using osu.Game.Users;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
@ -30,11 +29,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(ScoreTableRowBackground), typeof(ScoreTableRowBackground),
}; };
private readonly Box background;
public TestSceneScoresContainer() public TestSceneScoresContainer()
{ {
ScoresContainer scoresContainer; TestScoresContainer scoresContainer;
Child = new Container Child = new Container
{ {
@ -44,108 +41,137 @@ namespace osu.Game.Tests.Visual.Online
Width = 0.8f, Width = 0.8f,
Children = new Drawable[] Children = new Drawable[]
{ {
background = new Box { RelativeSizeAxes = Axes.Both }, new Box
scoresContainer = new ScoresContainer(), {
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
scoresContainer = new TestScoresContainer(),
} }
}; };
var scores = new List<ScoreInfo> var allScores = new APILegacyScores
{ {
new ScoreInfo Scores = new List<APILegacyScoreInfo>
{ {
User = new User new APILegacyScoreInfo
{ {
Id = 6602580, User = new User
Username = @"waaiiru",
Country = new Country
{ {
FullName = @"Spain", Id = 6602580,
FlagName = @"ES", Username = @"waaiiru",
Country = new Country
{
FullName = @"Spain",
FlagName = @"ES",
},
}, },
}, Mods = new Mod[]
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
new OsuModFlashlight(),
new OsuModHardRock(),
},
Rank = ScoreRank.XH,
PP = 200,
MaxCombo = 1234,
TotalScore = 1234567890,
Accuracy = 1,
},
new ScoreInfo
{
User = new User
{
Id = 4608074,
Username = @"Skycries",
Country = new Country
{ {
FullName = @"Brazil", new OsuModDoubleTime(),
FlagName = @"BR", new OsuModHidden(),
new OsuModFlashlight(),
new OsuModHardRock(),
}, },
Rank = ScoreRank.XH,
PP = 200,
MaxCombo = 1234,
TotalScore = 1234567890,
Accuracy = 1,
}, },
Mods = new Mod[] new APILegacyScoreInfo
{ {
new OsuModDoubleTime(), User = new User
new OsuModHidden(),
new OsuModFlashlight(),
},
Rank = ScoreRank.S,
PP = 190,
MaxCombo = 1234,
TotalScore = 1234789,
Accuracy = 0.9997,
},
new ScoreInfo
{
User = new User
{
Id = 1014222,
Username = @"eLy",
Country = new Country
{ {
FullName = @"Japan", Id = 4608074,
FlagName = @"JP", Username = @"Skycries",
Country = new Country
{
FullName = @"Brazil",
FlagName = @"BR",
},
}, },
}, Mods = new Mod[]
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
},
Rank = ScoreRank.B,
PP = 180,
MaxCombo = 1234,
TotalScore = 12345678,
Accuracy = 0.9854,
},
new ScoreInfo
{
User = new User
{
Id = 1541390,
Username = @"Toukai",
Country = new Country
{ {
FullName = @"Canada", new OsuModDoubleTime(),
FlagName = @"CA", new OsuModHidden(),
new OsuModFlashlight(),
}, },
Rank = ScoreRank.S,
PP = 190,
MaxCombo = 1234,
TotalScore = 1234789,
Accuracy = 0.9997,
}, },
Mods = new Mod[] new APILegacyScoreInfo
{ {
new OsuModDoubleTime(), User = new User
{
Id = 1014222,
Username = @"eLy",
Country = new Country
{
FullName = @"Japan",
FlagName = @"JP",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
},
Rank = ScoreRank.B,
PP = 180,
MaxCombo = 1234,
TotalScore = 12345678,
Accuracy = 0.9854,
}, },
Rank = ScoreRank.C, new APILegacyScoreInfo
PP = 170, {
MaxCombo = 1234, User = new User
TotalScore = 1234567, {
Accuracy = 0.8765, Id = 1541390,
}, Username = @"Toukai",
new ScoreInfo Country = new Country
{
FullName = @"Canada",
FlagName = @"CA",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
},
Rank = ScoreRank.C,
PP = 170,
MaxCombo = 1234,
TotalScore = 1234567,
Accuracy = 0.8765,
},
new APILegacyScoreInfo
{
User = new User
{
Id = 7151382,
Username = @"Mayuri Hana",
Country = new Country
{
FullName = @"Thailand",
FlagName = @"TH",
},
},
Rank = ScoreRank.D,
PP = 160,
MaxCombo = 1234,
TotalScore = 123456,
Accuracy = 0.6543,
},
}
};
var myBestScore = new APILegacyUserTopScoreInfo
{
Score = new APILegacyScoreInfo
{ {
User = new User User = new User
{ {
@ -163,9 +189,42 @@ namespace osu.Game.Tests.Visual.Online
TotalScore = 123456, TotalScore = 123456,
Accuracy = 0.6543, Accuracy = 0.6543,
}, },
Position = 1337,
}; };
foreach (var s in scores) var oneScore = new APILegacyScores
{
Scores = new List<APILegacyScoreInfo>
{
new APILegacyScoreInfo
{
User = new User
{
Id = 6602580,
Username = @"waaiiru",
Country = new Country
{
FullName = @"Spain",
FlagName = @"ES",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
new OsuModFlashlight(),
new OsuModHardRock(),
},
Rank = ScoreRank.XH,
PP = 200,
MaxCombo = 1234,
TotalScore = 1234567890,
Accuracy = 1,
}
}
};
foreach (var s in allScores.Scores)
{ {
s.Statistics.Add(HitResult.Great, RNG.Next(2000)); s.Statistics.Add(HitResult.Great, RNG.Next(2000));
s.Statistics.Add(HitResult.Good, RNG.Next(2000)); s.Statistics.Add(HitResult.Good, RNG.Next(2000));
@ -173,15 +232,26 @@ namespace osu.Game.Tests.Visual.Online
s.Statistics.Add(HitResult.Miss, RNG.Next(2000)); s.Statistics.Add(HitResult.Miss, RNG.Next(2000));
} }
AddStep("Load all scores", () => scoresContainer.Scores = scores); AddStep("Load all scores", () =>
{
allScores.UserScore = null;
scoresContainer.Scores = allScores;
});
AddStep("Load null scores", () => scoresContainer.Scores = null); AddStep("Load null scores", () => scoresContainer.Scores = null);
AddStep("Load only one score", () => scoresContainer.Scores = new[] { scores.First() }); AddStep("Load only one score", () => scoresContainer.Scores = oneScore);
AddStep("Load scores with my best", () =>
{
allScores.UserScore = myBestScore;
scoresContainer.Scores = allScores;
});
} }
[BackgroundDependencyLoader] private class TestScoresContainer : ScoresContainer
private void load(OsuColour colours)
{ {
background.Colour = colours.Gray2; public new APILegacyScores Scores
{
set => base.Scores = value;
}
} }
} }
} }

View File

@ -133,6 +133,9 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; }); AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; }); AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; });
AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; }); AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; });
AddStep(@"Sort by DateAdded", delegate { songSelect.FilterControl.Sort = SortMode.DateAdded; });
AddStep(@"Sort by BPM", delegate { songSelect.FilterControl.Sort = SortMode.BPM; });
AddStep(@"Sort by Length", delegate { songSelect.FilterControl.Sort = SortMode.Length; });
AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
} }
@ -265,16 +268,21 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
int beatmapId = setId * 10 + i; int beatmapId = setId * 10 + i;
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
beatmaps.Add(new BeatmapInfo beatmaps.Add(new BeatmapInfo
{ {
Ruleset = getRuleset(), Ruleset = getRuleset(),
OnlineBeatmapID = beatmapId, OnlineBeatmapID = beatmapId,
Path = "normal.osu", Path = "normal.osu",
Version = $"{beatmapId}", Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
Length = length,
BPM = bpm,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
OverallDifficulty = 3.5f, OverallDifficulty = 3.5f,
} },
}); });
} }
@ -286,10 +294,11 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
// Create random metadata, then we can check if sorting works based on these // Create random metadata, then we can check if sorting works based on these
Artist = "Some Artist " + RNG.Next(0, 9), Artist = "Some Artist " + RNG.Next(0, 9),
Title = $"Some Song (set id {setId})", Title = $"Some Song (set id {setId}, max bpm {beatmaps.Max(b => b.BPM):0.#})",
AuthorString = "Some Guy " + RNG.Next(0, 9), AuthorString = "Some Guy " + RNG.Next(0, 9),
}, },
Beatmaps = beatmaps Beatmaps = beatmaps,
DateAdded = DateTimeOffset.UtcNow,
}; };
} }
} }

View File

@ -14,8 +14,6 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneBackButton : OsuTestScene public class TestSceneBackButton : OsuTestScene
{ {
private readonly BackButton button;
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
typeof(TwoLayerButton) typeof(TwoLayerButton)
@ -23,6 +21,8 @@ namespace osu.Game.Tests.Visual.UserInterface
public TestSceneBackButton() public TestSceneBackButton()
{ {
BackButton button;
Child = new Container Child = new Container
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
@ -40,11 +40,12 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Action = () => button.Hide(),
} }
} }
}; };
button.Action = () => button.Hide();
AddStep("show button", () => button.Show()); AddStep("show button", () => button.Show());
AddStep("hide button", () => button.Hide()); AddStep("hide button", () => button.Hide());
} }

View File

@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.UserInterface
}, },
}; };
breadcrumbs.Current.ValueChanged += screen => titleText.Text = $"Changed to {screen.NewValue.ToString()}"; breadcrumbs.Current.ValueChanged += screen => titleText.Text = $"Changed to {screen.NewValue}";
breadcrumbs.Current.TriggerChange(); breadcrumbs.Current.TriggerChange();
waitForCurrent(); waitForCurrent();

View File

@ -159,7 +159,7 @@ namespace osu.Game.Tournament.Components
} }
var bpm = beatmap.BeatmapSet.OnlineInfo.BPM; var bpm = beatmap.BeatmapSet.OnlineInfo.BPM;
var length = beatmap.OnlineInfo.Length; var length = beatmap.Length;
string hardRockExtra = ""; string hardRockExtra = "";
string srExtra = ""; string srExtra = "";
@ -180,7 +180,7 @@ namespace osu.Game.Tournament.Components
panelContents.Children = new Drawable[] panelContents.Children = new Drawable[]
{ {
new DiffPiece(("Length", TimeSpan.FromSeconds(length).ToString(@"mm\:ss"))) new DiffPiece(("Length", TimeSpan.FromMilliseconds(length).ToString(@"mm\:ss")))
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,

View File

@ -51,6 +51,16 @@ namespace osu.Game.Beatmaps
[NotMapped] [NotMapped]
public BeatmapOnlineInfo OnlineInfo { get; set; } public BeatmapOnlineInfo OnlineInfo { get; set; }
/// <summary>
/// The playable length in milliseconds of this beatmap.
/// </summary>
public double Length { get; set; }
/// <summary>
/// The most common BPM of this beatmap.
/// </summary>
public double BPM { get; set; }
public string Path { get; set; } public string Path { get; set; }
[JsonProperty("file_sha2")] [JsonProperty("file_sha2")]

View File

@ -23,6 +23,7 @@ using osu.Game.IO.Archives;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
@ -302,6 +303,8 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Ruleset = ruleset; beatmap.BeatmapInfo.Ruleset = ruleset;
// TODO: this should be done in a better place once we actually need to dynamically update it. // TODO: this should be done in a better place once we actually need to dynamically update it.
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0;
beatmap.BeatmapInfo.Length = calculateLength(beatmap);
beatmap.BeatmapInfo.BPM = beatmap.ControlPointInfo.BPMMode;
beatmapInfos.Add(beatmap.BeatmapInfo); beatmapInfos.Add(beatmap.BeatmapInfo);
} }
@ -310,6 +313,19 @@ namespace osu.Game.Beatmaps
return beatmapInfos; return beatmapInfos;
} }
private double calculateLength(IBeatmap b)
{
if (!b.HitObjects.Any())
return 0;
var lastObject = b.HitObjects.Last();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime;
double startTime = b.HitObjects.First().StartTime;
return endTime - startTime;
}
/// <summary> /// <summary>
/// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation.
/// </summary> /// </summary>

View File

@ -8,11 +8,6 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public class BeatmapOnlineInfo public class BeatmapOnlineInfo
{ {
/// <summary>
/// The length in milliseconds of this beatmap's song.
/// </summary>
public double Length { get; set; }
/// <summary> /// <summary>
/// The amount of circles in this beatmap. /// The amount of circles in this beatmap.
/// </summary> /// </summary>

View File

@ -35,8 +35,21 @@ namespace osu.Game.Beatmaps
[NotMapped] [NotMapped]
public BeatmapSetMetrics Metrics { get; set; } public BeatmapSetMetrics Metrics { get; set; }
/// <summary>
/// The maximum star difficulty of all beatmaps in this set.
/// </summary>
public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0; public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0;
/// <summary>
/// The maximum playable length in milliseconds of all beatmaps in this set.
/// </summary>
public double MaxLength => Beatmaps?.Max(b => b.Length) ?? 0;
/// <summary>
/// The maximum BPM of all beatmaps in this set.
/// </summary>
public double MaxBPM => Beatmaps?.Max(b => b.BPM) ?? 0;
[NotMapped] [NotMapped]
public bool DeletePending { get; set; } public bool DeletePending { get; set; }

View File

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

View File

@ -0,0 +1,33 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class AddBPMAndLengthColumns : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<double>(
name: "BPM",
table: "BeatmapInfo",
nullable: false,
defaultValue: 0.0);
migrationBuilder.AddColumn<double>(
name: "Length",
table: "BeatmapInfo",
nullable: false,
defaultValue: 0.0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "BPM",
table: "BeatmapInfo");
migrationBuilder.DropColumn(
name: "Length",
table: "BeatmapInfo");
}
}
}

View File

@ -45,6 +45,8 @@ namespace osu.Game.Migrations
b.Property<int>("AudioLeadIn"); b.Property<int>("AudioLeadIn");
b.Property<double>("BPM");
b.Property<int>("BaseDifficultyID"); b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor"); b.Property<int>("BeatDivisor");
@ -61,6 +63,8 @@ namespace osu.Game.Migrations
b.Property<bool>("Hidden"); b.Property<bool>("Hidden");
b.Property<double>("Length");
b.Property<bool>("LetterboxInBreaks"); b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash"); b.Property<string>("MD5Hash");

View File

@ -42,6 +42,14 @@ namespace osu.Game.Online.API.Requests
score.Beatmap = beatmap; score.Beatmap = beatmap;
score.Ruleset = ruleset; score.Ruleset = ruleset;
} }
var userScore = r.UserScore;
if (userScore != null)
{
userScore.Score.Beatmap = beatmap;
userScore.Score.Ruleset = ruleset;
}
} }
protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores{createQueryParameters()}"; protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores{createQueryParameters()}";

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -71,6 +72,7 @@ namespace osu.Game.Online.API.Requests.Responses
StarDifficulty = starDifficulty, StarDifficulty = starDifficulty,
OnlineBeatmapID = OnlineBeatmapID, OnlineBeatmapID = OnlineBeatmapID,
Version = version, Version = version,
Length = TimeSpan.FromSeconds(length).TotalMilliseconds,
Status = Status, Status = Status,
BeatmapSet = set, BeatmapSet = set,
Metrics = metrics, Metrics = metrics,
@ -85,7 +87,6 @@ namespace osu.Game.Online.API.Requests.Responses
{ {
PlayCount = playCount, PlayCount = playCount,
PassCount = passCount, PassCount = passCount,
Length = length,
CircleCount = circleCount, CircleCount = circleCount,
SliderCount = sliderCount, SliderCount = sliderCount,
}, },

View File

@ -10,5 +10,17 @@ namespace osu.Game.Online.API.Requests.Responses
{ {
[JsonProperty(@"scores")] [JsonProperty(@"scores")]
public List<APILegacyScoreInfo> Scores; public List<APILegacyScoreInfo> Scores;
[JsonProperty(@"userScore")]
public APILegacyUserTopScoreInfo UserScore;
}
public class APILegacyUserTopScoreInfo
{
[JsonProperty(@"position")]
public int Position;
[JsonProperty(@"score")]
public APILegacyScoreInfo Score;
} }
} }

View File

@ -282,11 +282,9 @@ namespace osu.Game
performFromMainMenu(() => performFromMainMenu(() =>
{ {
Ruleset.Value = databasedScoreInfo.Ruleset;
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
Mods.Value = databasedScoreInfo.Mods;
menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore))); menuScreen.Push(new ReplayPlayerLoader(databasedScore));
}, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true); }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);
} }

View File

@ -60,7 +60,7 @@ namespace osu.Game.Overlays.BeatmapSet
} }
else else
{ {
length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss"); length.Value = TimeSpan.FromMilliseconds(beatmap.Length).ToString(@"m\:ss");
circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString();
sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString();
} }

View File

@ -23,10 +23,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private Color4 backgroundHoveredColour; private Color4 backgroundHoveredColour;
private readonly Box background; private readonly Box background;
private readonly TopScoreUserSection userSection;
private readonly TopScoreStatisticsSection statisticsSection;
public DrawableTopScore() public DrawableTopScore(ScoreInfo score, int position = 1)
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
@ -61,16 +59,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{ {
new Drawable[] new Drawable[]
{ {
userSection = new TopScoreUserSection new TopScoreUserSection
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Score = score,
ScorePosition = position,
}, },
null, null,
statisticsSection = new TopScoreStatisticsSection new TopScoreStatisticsSection
{ {
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
Score = score,
} }
}, },
}, },
@ -91,18 +92,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
background.Colour = backgroundIdleColour; background.Colour = backgroundIdleColour;
} }
/// <summary>
/// Sets the score to be displayed.
/// </summary>
public ScoreInfo Score
{
set
{
userSection.Score = value;
statisticsSection.Score = value;
}
}
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)
{ {
background.FadeColour(backgroundHoveredColour, fade_duration, Easing.OutQuint); background.FadeColour(backgroundHoveredColour, fade_duration, Easing.OutQuint);

View File

@ -59,7 +59,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Content = null; Content = null;
backgroundFlow.Clear(); backgroundFlow.Clear();
if (value == null || !value.Any()) if (value?.Any() != true)
return; return;
for (int i = 0; i < value.Count; i++) for (int i = 0; i < value.Count; i++)

View File

@ -5,15 +5,14 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osuTK;
using System.Linq;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Beatmaps;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osuTK;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Scoring;
namespace osu.Game.Overlays.BeatmapSet.Scores namespace osu.Game.Overlays.BeatmapSet.Scores
{ {
@ -24,18 +23,65 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly Box background; private readonly Box background;
private readonly ScoreTable scoreTable; private readonly ScoreTable scoreTable;
private readonly FillFlowContainer topScoresContainer;
private readonly DrawableTopScore topScore;
private readonly LoadingAnimation loadingAnimation; private readonly LoadingAnimation loadingAnimation;
[Resolved] [Resolved]
private IAPIProvider api { get; set; } private IAPIProvider api { get; set; }
private GetScoresRequest getScoresRequest;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get => beatmap;
set
{
if (beatmap == value)
return;
beatmap = value;
getScores(beatmap);
}
}
protected APILegacyScores Scores
{
set
{
Schedule(() =>
{
loading = false;
topScoresContainer.Clear();
if (value?.Scores.Any() != true)
{
scoreTable.Scores = null;
scoreTable.Hide();
return;
}
scoreTable.Scores = value.Scores;
scoreTable.Show();
var topScore = value.Scores.First();
var userScore = value.UserScore;
topScoresContainer.Add(new DrawableTopScore(topScore));
if (userScore != null && userScore.Score.OnlineScoreID != topScore.OnlineScoreID)
topScoresContainer.Add(new DrawableTopScore(userScore.Score, userScore.Position));
});
}
}
public ScoresContainer() public ScoresContainer()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
background = new Box background = new Box
@ -54,7 +100,13 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Margin = new MarginPadding { Vertical = spacing }, Margin = new MarginPadding { Vertical = spacing },
Children = new Drawable[] Children = new Drawable[]
{ {
topScore = new DrawableTopScore(), topScoresContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
},
scoreTable = new ScoreTable scoreTable = new ScoreTable
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
@ -65,7 +117,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
loadingAnimation = new LoadingAnimation loadingAnimation = new LoadingAnimation
{ {
Alpha = 0, Alpha = 0,
Margin = new MarginPadding(20) Margin = new MarginPadding(20),
}, },
}; };
} }
@ -74,7 +126,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
background.Colour = colours.Gray2; background.Colour = colours.Gray2;
updateDisplay();
} }
private bool loading private bool loading
@ -82,62 +133,23 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration); set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration);
} }
private GetScoresRequest getScoresRequest; private void getScores(BeatmapInfo beatmap)
private IReadOnlyList<ScoreInfo> scores;
public IReadOnlyList<ScoreInfo> Scores
{
get => scores;
set
{
getScoresRequest?.Cancel();
scores = value;
updateDisplay();
}
}
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get => beatmap;
set
{
beatmap = value;
Scores = null;
if (beatmap?.OnlineBeatmapID.HasValue != true)
return;
loading = true;
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
getScoresRequest.Success += r => Schedule(() => Scores = r.Scores);
api.Queue(getScoresRequest);
}
}
private void updateDisplay()
{
loading = false;
scoreTable.Scores = scores?.Count > 1 ? scores : new List<ScoreInfo>();
scoreTable.FadeTo(scores?.Count > 1 ? 1 : 0);
if (scores?.Any() == true)
{
topScore.Score = scores.FirstOrDefault();
topScore.Show();
}
else
topScore.Hide();
}
protected override void Dispose(bool isDisposing)
{ {
getScoresRequest?.Cancel(); getScoresRequest?.Cancel();
getScoresRequest = null;
Scores = null;
if (beatmap?.OnlineBeatmapID.HasValue != true)
{
loading = false;
return;
}
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
getScoresRequest.Success += scores => Scores = scores;
api.Queue(getScoresRequest);
loading = true;
} }
} }
} }

View File

@ -39,19 +39,28 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Spacing = new Vector2(10, 0), Spacing = new Vector2(10, 0),
Children = new Drawable[] Children = new Drawable[]
{ {
rankText = new OsuSpriteText new FillFlowContainer
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Text = "#1", AutoSizeAxes = Axes.Both,
Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) Direction = FillDirection.Vertical,
}, Children = new Drawable[]
rank = new UpdateableRank(ScoreRank.D) {
{ rankText = new OsuSpriteText
Anchor = Anchor.Centre, {
Origin = Anchor.Centre, Anchor = Anchor.Centre,
Size = new Vector2(40), Origin = Anchor.Centre,
FillMode = FillMode.Fit, Font = OsuFont.GetFont(size: 24, weight: FontWeight.Bold, italics: true)
},
rank = new UpdateableRank(ScoreRank.D)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(40),
FillMode = FillMode.Fit,
},
}
}, },
avatar = new UpdateableAvatar(hideImmediately: true) avatar = new UpdateableAvatar(hideImmediately: true)
{ {
@ -109,6 +118,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
rankText.Colour = colours.Yellow; rankText.Colour = colours.Yellow;
} }
public int ScorePosition
{
set => rankText.Text = $"#{value}";
}
/// <summary> /// <summary>
/// Sets the score to be displayed. /// Sets the score to be displayed.
/// </summary> /// </summary>

View File

@ -21,12 +21,9 @@ namespace osu.Game.Overlays
{ {
public class BeatmapSetOverlay : FullscreenOverlay public class BeatmapSetOverlay : FullscreenOverlay
{ {
private const int fade_duration = 300;
public const float X_PADDING = 40; public const float X_PADDING = 40;
public const float TOP_PADDING = 25; public const float TOP_PADDING = 25;
public const float RIGHT_WIDTH = 275; public const float RIGHT_WIDTH = 275;
protected readonly Header Header; protected readonly Header Header;
private RulesetStore rulesets; private RulesetStore rulesets;
@ -40,7 +37,7 @@ namespace osu.Game.Overlays
{ {
OsuScrollContainer scroll; OsuScrollContainer scroll;
Info info; Info info;
ScoresContainer scores; ScoresContainer scoreContainer;
Children = new Drawable[] Children = new Drawable[]
{ {
@ -62,7 +59,7 @@ namespace osu.Game.Overlays
{ {
Header = new Header(), Header = new Header(),
info = new Info(), info = new Info(),
scores = new ScoresContainer(), scoreContainer = new ScoresContainer(),
}, },
}, },
}, },
@ -74,7 +71,7 @@ namespace osu.Game.Overlays
Header.Picker.Beatmap.ValueChanged += b => Header.Picker.Beatmap.ValueChanged += b =>
{ {
info.Beatmap = b.NewValue; info.Beatmap = b.NewValue;
scores.Beatmap = b.NewValue; scoreContainer.Beatmap = b.NewValue;
scroll.ScrollToStart(); scroll.ScrollToStart();
}; };
@ -101,6 +98,7 @@ namespace osu.Game.Overlays
public void FetchAndShowBeatmap(int beatmapId) public void FetchAndShowBeatmap(int beatmapId)
{ {
beatmapSet.Value = null; beatmapSet.Value = null;
var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId);
req.Success += res => req.Success += res =>
{ {
@ -108,15 +106,18 @@ namespace osu.Game.Overlays
Header.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); Header.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
}; };
API.Queue(req); API.Queue(req);
Show(); Show();
} }
public void FetchAndShowBeatmapSet(int beatmapSetId) public void FetchAndShowBeatmapSet(int beatmapSetId)
{ {
beatmapSet.Value = null; beatmapSet.Value = null;
var req = new GetBeatmapSetRequest(beatmapSetId); var req = new GetBeatmapSetRequest(beatmapSetId);
req.Success += res => beatmapSet.Value = res.ToBeatmapSet(rulesets); req.Success += res => beatmapSet.Value = res.ToBeatmapSet(rulesets);
API.Queue(req); API.Queue(req);
Show(); Show();
} }

View File

@ -55,6 +55,8 @@ namespace osu.Game.Overlays
private Container dragContainer; private Container dragContainer;
private Container playerContainer; private Container playerContainer;
public bool IsUserPaused { get; private set; }
[Resolved] [Resolved]
private Bindable<WorkingBeatmap> beatmap { get; set; } private Bindable<WorkingBeatmap> beatmap { get; set; }
@ -157,7 +159,7 @@ namespace osu.Game.Overlays
Origin = Anchor.Centre, Origin = Anchor.Centre,
Scale = new Vector2(1.4f), Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f), IconScale = new Vector2(1.4f),
Action = play, Action = togglePause,
Icon = FontAwesome.Regular.PlayCircle, Icon = FontAwesome.Regular.PlayCircle,
}, },
nextButton = new MusicIconButton nextButton = new MusicIconButton
@ -276,7 +278,7 @@ namespace osu.Game.Overlays
} }
} }
private void play() private void togglePause()
{ {
var track = current?.Track; var track = current?.Track;
@ -288,9 +290,15 @@ namespace osu.Game.Overlays
} }
if (track.IsRunning) if (track.IsRunning)
{
IsUserPaused = true;
track.Stop(); track.Stop();
}
else else
{
track.Start(); track.Start();
IsUserPaused = false;
}
} }
private void prev() private void prev()

View File

@ -224,30 +224,32 @@ namespace osu.Game.Screens.Edit
public override void OnResuming(IScreen last) public override void OnResuming(IScreen last)
{ {
Beatmap.Value.Track?.Stop();
base.OnResuming(last); base.OnResuming(last);
Beatmap.Value.Track?.Stop();
} }
public override void OnEntering(IScreen last) public override void OnEntering(IScreen last)
{ {
base.OnEntering(last); base.OnEntering(last);
Background.FadeColour(Color4.DarkGray, 500); Background.FadeColour(Color4.DarkGray, 500);
Beatmap.Value.Track?.Stop(); resetTrack();
} }
public override bool OnExiting(IScreen next) public override bool OnExiting(IScreen next)
{ {
Background.FadeColour(Color4.White, 500); Background.FadeColour(Color4.White, 500);
resetTrack();
if (Beatmap.Value.Track != null)
{
Beatmap.Value.Track.Tempo.Value = 1;
Beatmap.Value.Track.Start();
}
return base.OnExiting(next); return base.OnExiting(next);
} }
private void resetTrack()
{
Beatmap.Value.Track?.ResetSpeedAdjustments();
Beatmap.Value.Track?.Stop();
}
private void exportBeatmap() => host.OpenFileExternally(Beatmap.Value.Save()); private void exportBeatmap() => host.OpenFileExternally(Beatmap.Value.Save());
private void onModeChanged(ValueChangedEvent<EditorScreenMode> e) private void onModeChanged(ValueChangedEvent<EditorScreenMode> e)

View File

@ -42,6 +42,9 @@ namespace osu.Game.Screens.Menu
[Resolved] [Resolved]
private GameHost host { get; set; } private GameHost host { get; set; }
[Resolved(canBeNull: true)]
private MusicController music { get; set; }
private BackgroundScreenDefault background; private BackgroundScreenDefault background;
protected override BackgroundScreen CreateBackground() => background; protected override BackgroundScreen CreateBackground() => background;
@ -189,6 +192,9 @@ namespace osu.Game.Screens.Menu
//we may have consumed our preloaded instance, so let's make another. //we may have consumed our preloaded instance, so let's make another.
preloadSongSelect(); preloadSongSelect();
if (Beatmap.Value.Track != null && music?.IsUserPaused != true)
Beatmap.Value.Track.Start();
} }
public override bool OnExiting(IScreen next) public override bool OnExiting(IScreen next)

View File

@ -0,0 +1,34 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Game.Scoring;
namespace osu.Game.Screens.Play
{
public class ReplayPlayerLoader : PlayerLoader
{
private readonly ScoreInfo scoreInfo;
public ReplayPlayerLoader(Score score)
: base(() => new ReplayPlayer(score))
{
if (score.Replay == null)
throw new ArgumentNullException(nameof(score.Replay), $"{nameof(score)} must have a non-null {nameof(score.Replay)}.");
scoreInfo = score.ScoreInfo;
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = base.CreateChildDependencies(parent);
// these will be reverted thanks to PlayerLoader's lease.
Mods.Value = scoreInfo.Mods;
Ruleset.Value = scoreInfo.Ruleset;
return dependencies;
}
}
}

View File

@ -18,8 +18,6 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
@ -289,14 +287,11 @@ namespace osu.Game.Screens.Select
if (b?.HitObjects?.Any() == true) if (b?.HitObjects?.Any() == true)
{ {
HitObject lastObject = b.HitObjects.LastOrDefault();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
labels.Add(new InfoLabel(new BeatmapStatistic labels.Add(new InfoLabel(new BeatmapStatistic
{ {
Name = "Length", Name = "Length",
Icon = FontAwesome.Regular.Clock, Icon = FontAwesome.Regular.Clock,
Content = TimeSpan.FromMilliseconds(endTime - b.HitObjects.First().StartTime).ToString(@"m\:ss"), Content = TimeSpan.FromMilliseconds(b.BeatmapInfo.Length).ToString(@"m\:ss"),
})); }));
labels.Add(new InfoLabel(new BeatmapStatistic labels.Add(new InfoLabel(new BeatmapStatistic

View File

@ -48,6 +48,12 @@ namespace osu.Game.Screens.Select.Carousel
case SortMode.DateAdded: case SortMode.DateAdded:
return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded); return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded);
case SortMode.BPM:
return BeatmapSet.MaxBPM.CompareTo(otherSet.BeatmapSet.MaxBPM);
case SortMode.Length:
return BeatmapSet.MaxLength.CompareTo(otherSet.BeatmapSet.MaxLength);
case SortMode.Difficulty: case SortMode.Difficulty:
return BeatmapSet.MaxStarDifficulty.CompareTo(otherSet.BeatmapSet.MaxStarDifficulty); return BeatmapSet.MaxStarDifficulty.CompareTo(otherSet.BeatmapSet.MaxStarDifficulty);
} }

View File

@ -87,6 +87,9 @@ namespace osu.Game.Screens.Select
private readonly Bindable<RulesetInfo> decoupledRuleset = new Bindable<RulesetInfo>(); private readonly Bindable<RulesetInfo> decoupledRuleset = new Bindable<RulesetInfo>();
[Resolved(canBeNull: true)]
private MusicController music { get; set; }
[Cached] [Cached]
[Cached(Type = typeof(IBindable<IReadOnlyList<Mod>>))] [Cached(Type = typeof(IBindable<IReadOnlyList<Mod>>))]
private readonly Bindable<IReadOnlyList<Mod>> mods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>()); // Bound to the game's mods, but is not reset on exiting private readonly Bindable<IReadOnlyList<Mod>> mods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>()); // Bound to the game's mods, but is not reset on exiting
@ -570,7 +573,7 @@ namespace osu.Game.Screens.Select
{ {
Track track = Beatmap.Value.Track; Track track = Beatmap.Value.Track;
if (!track.IsRunning || restart) if ((!track.IsRunning || restart) && music?.IsUserPaused != true)
{ {
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
track.Restart(); track.Restart();