Merge https://github.com/ppy/osu into online-beatmap-set-overlay

This commit is contained in:
DrabWeb
2017-09-11 01:24:52 -03:00
66 changed files with 1105 additions and 662 deletions

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Desktop.Deploy</RootNamespace>
<AssemblyName>osu.Desktop.Deploy</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>

View File

@ -12,50 +12,104 @@ namespace osu.Desktop.Tests.Visual
{
public override string Description => "BeatmapDetails tab of BeatmapDetailArea";
private readonly BeatmapDetails details;
public TestCaseBeatmapDetails()
{
BeatmapDetails details;
Add(details = new BeatmapDetails
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(150),
Beatmap = new BeatmapInfo
});
AddStep("beatmap all metrics", () => details.Beatmap = new BeatmapInfo
{
Version = "All Metrics",
Metadata = new BeatmapMetadata
{
Version = "VisualTest",
Metadata = new BeatmapMetadata
{
Source = "Some guy",
Tags = "beatmap metadata example with a very very long list of tags and not much creativity",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 7,
ApproachRate = 3.5f,
OverallDifficulty = 5.7f,
DrainRate = 1,
},
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 10),
Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6),
},
Source = "osu!lazer",
Tags = "this beatmap has all the metrics",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 7,
DrainRate = 1,
OverallDifficulty = 5.7f,
ApproachRate = 3.5f,
},
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 10),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
},
});
AddRepeatStep("fail values", newRetryAndFailValues, 10);
}
AddStep("beatmap ratings", () => details.Beatmap = new BeatmapInfo
{
Version = "Only Ratings",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has ratings metrics but not retries or fails",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 6,
DrainRate = 9,
OverallDifficulty = 6,
ApproachRate = 6,
},
StarDifficulty = 4.8f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 10),
},
});
private int lastRange = 1;
AddStep("beatmap fails retries", () => details.Beatmap = new BeatmapInfo
{
Version = "Only Retries and Fails",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has retries and fails but no ratings",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 3.7f,
DrainRate = 6,
OverallDifficulty = 6,
ApproachRate = 7,
},
StarDifficulty = 2.91f,
Metrics = new BeatmapMetrics
{
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
},
});
private void newRetryAndFailValues()
{
details.Beatmap.Metrics.Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6);
details.Beatmap.Metrics.Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6);
details.Beatmap = details.Beatmap;
lastRange += 100;
AddStep("beatmap no metrics", () => details.Beatmap = new BeatmapInfo
{
Version = "No Metrics",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has no metrics",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 5,
DrainRate = 5,
OverallDifficulty = 5.5f,
ApproachRate = 6.5f,
},
StarDifficulty = 1.97f,
Metrics = new BeatmapMetrics(),
});
AddStep("null beatmap", () => details.Beatmap = null);
}
}
}
}

View File

@ -41,12 +41,14 @@ namespace osu.Desktop.Tests.Visual
{
new BeatmapSetInfo
{
OnlineBeatmapSetID = 578332,
Metadata = new BeatmapMetadata
{
Title = @"OrVid",
Artist = @"An",
Author = @"RLC",
Source = @"",
Tags = @"acuticnotes an-fillnote revid tear tearvid encrpted encryption axi axivid quad her hervid recoll",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@ -71,12 +73,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 599627,
Metadata = new BeatmapMetadata
{
Title = @"tiny lamp",
Artist = @"fhana",
Author = @"Sotarks",
Source = @"ぎんぎつね",
Tags = @"lantis junichi sato yuxuki waga kevin mitsunaga towana gingitsune opening op full ver version kalibe collab collaboration",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@ -101,12 +105,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 513268,
Metadata = new BeatmapMetadata
{
Title = @"At Gwanghwamun",
Artist = @"KYUHYUN",
Author = @"Cerulean Veyron",
Source = @"",
Tags = @"soul ballad kh super junior sj suju 슈퍼주니어 kt뮤직 sm엔터테인먼트 s.m.entertainment kt music 1st mini album ep",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@ -146,12 +152,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 586841,
Metadata = new BeatmapMetadata
{
Title = @"RHAPSODY OF BLUE SKY",
Artist = @"fhana",
Author = @"[Kamiya]",
Source = @"小林さんちのメイドラゴン",
Tags = @"kobayashi san chi no maidragon aozora no opening anime maid dragon oblivion karen dynamix imoutosan pata-mon gxytcgxytc",
},
OnlineInfo = new BeatmapSetOnlineInfo
{

View File

@ -1,13 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
using OpenTK;
namespace osu.Desktop.Tests.Visual
{
@ -24,7 +24,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.XH,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -42,7 +42,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.X,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -60,7 +60,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.SH,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -78,7 +78,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.S,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -96,7 +96,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.A,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -114,7 +114,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.B,
Accuracy = 98.26,
Accuracy = 0.9826,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -132,7 +132,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.C,
Accuracy = 96.54,
Accuracy = 0.9654,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -150,7 +150,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
Accuracy = 60.25,
Accuracy = 0.6025,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -168,7 +168,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
Accuracy = 51.40,
Accuracy = 0.5140,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -186,7 +186,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
Accuracy = 42.22,
Accuracy = 0.4222,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },

View File

@ -32,7 +32,7 @@ namespace osu.Desktop.Tests.Visual
backingDatabase.CreateTable<StoreVersion>();
rulesets = new RulesetStore(backingDatabase);
manager = new BeatmapManager(storage, null, backingDatabase, rulesets);
manager = new BeatmapManager(storage, null, backingDatabase, rulesets, null);
for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(i));

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Desktop.Tests</RootNamespace>
<AssemblyName>osu.Desktop.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

View File

@ -22,7 +22,7 @@
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
<SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>

View File

@ -22,7 +22,7 @@
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
<SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>

View File

@ -93,8 +93,6 @@ namespace osu.Game.Rulesets.Catch
}
}
public override Mod GetAutoplayMod() => new ModAutoplay();
public override string Description => "osu!catch";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Catch</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Catch</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

View File

@ -105,8 +105,6 @@ namespace osu.Game.Rulesets.Mania
}
}
public override Mod GetAutoplayMod() => new ModAutoplay();
public override string Description => "osu!mania";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };

View File

@ -68,6 +68,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModFadeIn : Mod
{
public override string Name => "FadeIn";
public override string ShortenedName => "FI";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override double ScoreMultiplier => 1;
@ -78,12 +79,14 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModRandom : Mod
{
public override string Name => "Random";
public override string ShortenedName => "RD";
public override string Description => @"Shuffle around the notes!";
public override double ScoreMultiplier => 1;
}
public abstract class ManiaKeyMod : Mod
{
public override string ShortenedName => Name;
public abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true;
@ -146,6 +149,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKeyCoop : Mod
{
public override string Name => "KeyCoop";
public override string ShortenedName => "2P";
public override string Description => @"Double the key amount, double the fun!";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;

View File

@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModGravity : Mod, IGenerateSpeedAdjustments
{
public override string Name => "Gravity";
public override string ShortenedName => "GR";
public override double ScoreMultiplier => 0;

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Mania</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Mania</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

View File

@ -96,6 +96,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModSpunOut : Mod
{
public override string Name => "Spun Out";
public override string ShortenedName => "SO";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
public override string Description => @"Spinners will be automatically completed";
public override double ScoreMultiplier => 0.9;
@ -106,6 +107,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModAutopilot : Mod
{
public override string Name => "Autopilot";
public override string ShortenedName => "AP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 0;
@ -126,6 +128,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModTarget : Mod
{
public override string Name => "Target";
public override string ShortenedName => "TP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
public override string Description => @"";
public override double ScoreMultiplier => 1;

View File

@ -51,6 +51,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
foreach (OsuDifficultyHitObject h in onScreen)
{
// ReSharper disable once PossibleNullReferenceException (resharper not smart enough to understand IEnumerator.MoveNext())
h.TimeUntilHit -= latest.DeltaTime;
// Calculate reading strain here
}

View File

@ -112,8 +112,6 @@ namespace osu.Game.Rulesets.Osu
}
}
public override Mod GetAutoplayMod() => new OsuModAutoplay();
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap);

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Osu</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Osu</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>

View File

@ -95,8 +95,6 @@ namespace osu.Game.Rulesets.Taiko
}
}
public override Mod GetAutoplayMod() => new TaikoModAutoplay();
public override string Description => "osu!taiko";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Taiko</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Taiko</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

View File

@ -7,7 +7,7 @@
<OutputType>Library</OutputType>
<RootNamespace>osu.Game.Tests</RootNamespace>
<AssemblyName>osu.Game.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>

View File

@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps
public class Beatmap<T>
where T : HitObject
{
public BeatmapInfo BeatmapInfo;
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
public readonly List<Color4> ComboColors = new List<Color4>
@ -33,7 +33,7 @@ namespace osu.Game.Beatmaps
/// <summary>
/// The HitObjects this Beatmap contains.
/// </summary>
public List<T> HitObjects;
public List<T> HitObjects = new List<T>();
/// <summary>
/// Total amount of break time in the beatmap.
@ -44,12 +44,13 @@ namespace osu.Game.Beatmaps
/// Constructs a new beatmap.
/// </summary>
/// <param name="original">The original beatmap to use the parameters of.</param>
public Beatmap(Beatmap original = null)
public Beatmap(Beatmap<T> original = null)
{
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
Breaks = original?.Breaks ?? Breaks;
ComboColors = original?.ComboColors ?? ComboColors;
HitObjects = original?.HitObjects ?? HitObjects;
}
}
@ -65,7 +66,6 @@ namespace osu.Game.Beatmaps
public Beatmap(Beatmap original = null)
: base(original)
{
HitObjects = original?.HitObjects;
}
}
}

View File

@ -20,6 +20,9 @@ using osu.Game.IPC;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using SQLite.Net;
using osu.Game.Online.API.Requests;
using System.Threading.Tasks;
using osu.Game.Online.API;
namespace osu.Game.Beatmaps
{
@ -63,6 +66,10 @@ namespace osu.Game.Beatmaps
private readonly BeatmapStore beatmaps;
private readonly APIAccess api;
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private BeatmapIPCChannel ipc;
@ -76,7 +83,7 @@ namespace osu.Game.Beatmaps
/// </summary>
public Func<Storage> GetStableStorage { private get; set; }
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, IIpcHost importHost = null)
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
{
beatmaps = new BeatmapStore(connection);
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
@ -88,6 +95,7 @@ namespace osu.Game.Beatmaps
this.files = files;
this.connection = connection;
this.rulesets = rulesets;
this.api = api;
if (importHost != null)
ipc = new BeatmapIPCChannel(importHost, this);
@ -177,6 +185,74 @@ namespace osu.Game.Beatmaps
beatmaps.Add(beatmapSetInfo);
}
/// <summary>
/// Downloads a beatmap.
/// </summary>
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
/// <returns>A new <see cref="DownloadBeatmapSetRequest"/>, or an existing one if a download is already in progress.</returns>
public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo)
{
var existing = GetExistingDownload(beatmapSetInfo);
if (existing != null) return existing;
if (api == null) return null;
ProgressNotification downloadNotification = new ProgressNotification
{
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
};
var request = new DownloadBeatmapSetRequest(beatmapSetInfo);
request.DownloadProgressed += progress =>
{
downloadNotification.State = ProgressNotificationState.Active;
downloadNotification.Progress = progress;
};
request.Success += data =>
{
downloadNotification.State = ProgressNotificationState.Completed;
using (var stream = new MemoryStream(data))
using (var archive = new OszArchiveReader(stream))
Import(archive);
currentDownloads.Remove(request);
};
request.Failure += data =>
{
downloadNotification.State = ProgressNotificationState.Completed;
Logger.Error(data, "Failed to get beatmap download information");
currentDownloads.Remove(request);
};
downloadNotification.CancelRequested += () =>
{
request.Cancel();
currentDownloads.Remove(request);
downloadNotification.State = ProgressNotificationState.Cancelled;
return true;
};
currentDownloads.Add(request);
PostNotification?.Invoke(downloadNotification);
// don't run in the main api queue as this is a long-running task.
Task.Run(() => request.Perform(api));
return request;
}
/// <summary>
/// Get an existing download request if it exists.
/// </summary>
/// <param name="beatmap">The <see cref="BeatmapSetInfo"/> whose download request is wanted.</param>
/// <returns>The <see cref="DownloadBeatmapSetRequest"/> object if it exists, or null.</returns>
public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
/// <summary>
/// Delete a beatmap from the manager.
/// Is a no-op for already deleted beatmaps.

View File

@ -7,7 +7,6 @@ using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@ -42,10 +41,7 @@ namespace osu.Game.Beatmaps
this.game = game;
}
protected override Beatmap GetBeatmap() => new Beatmap
{
HitObjects = new List<HitObject>(),
};
protected override Beatmap GetBeatmap() => new Beatmap();
protected override Texture GetBackground() => game.Textures.Get(@"Backgrounds/bg4");
@ -59,8 +55,6 @@ namespace osu.Game.Beatmaps
{
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { };
public override Mod GetAutoplayMod() => new ModAutoplay();
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset)
{
throw new NotImplementedException();

View File

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Beatmaps.Formats
{
@ -21,7 +20,7 @@ namespace osu.Game.Beatmaps.Formats
{
string line;
do { line = stream.ReadLine()?.Trim(); }
while (line != null && line.Length == 0);
while (line != null && line.Length == 0);
if (line == null || !decoders.ContainsKey(line))
throw new IOException(@"Unknown file format");
@ -47,7 +46,6 @@ namespace osu.Game.Beatmaps.Formats
{
var beatmap = new Beatmap
{
HitObjects = new List<HitObject>(),
BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata(),

View File

@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps
{
if (beatmap != null) return beatmap;
beatmap = GetBeatmap();
beatmap = GetBeatmap() ?? new Beatmap();
// use the database-backed info.
beatmap.BeatmapInfo = BeatmapInfo;

View File

@ -96,7 +96,7 @@ namespace osu.Game.Graphics.UserInterface
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour };
#region DrawableOsuDropdownMenuItem
protected class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
{
private Color4? accentColour;
public Color4 AccentColour

View File

@ -15,11 +15,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
{
private readonly VolumeMeter volumeMeterMaster;
private void volumeChanged(double newVolume)
{
Show();
schedulePopOut();
}
protected override bool BlockPassThroughMouse => false;
public VolumeControl()
{
@ -85,6 +81,12 @@ namespace osu.Game.Graphics.UserInterface.Volume
return false;
}
private void volumeChanged(double newVolume)
{
Show();
schedulePopOut();
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{

View File

@ -0,0 +1,24 @@
// 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.Beatmaps;
using System;
namespace osu.Game.Online.API.Requests
{
public class DownloadBeatmapSetRequest : APIDownloadRequest
{
public readonly BeatmapSetInfo BeatmapSet;
public Action<float> DownloadProgressed;
public DownloadBeatmapSetRequest(BeatmapSetInfo set)
{
BeatmapSet = set;
Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total);
}
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download";
}
}

View File

@ -1,10 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.IO.Network;
using osu.Game.Beatmaps;
using osu.Game.Users;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Online.API.Requests
@ -16,6 +20,14 @@ namespace osu.Game.Online.API.Requests
public GetScoresRequest(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
Success += onSuccess;
}
private void onSuccess(GetScoresResponse r)
{
foreach (OnlineScore score in r.Scores)
score.ApplyBeatmap(beatmap);
}
protected override WebRequest CreateWebRequest()
@ -32,6 +44,88 @@ namespace osu.Game.Online.API.Requests
public class GetScoresResponse
{
[JsonProperty(@"scores")]
public IEnumerable<Score> Scores;
public IEnumerable<OnlineScore> Scores;
}
}
public class OnlineScore : Score
{
[JsonProperty(@"score")]
private double totalScore
{
set { TotalScore = value; }
}
[JsonProperty(@"max_combo")]
private int maxCombo
{
set { MaxCombo = value; }
}
[JsonProperty(@"user")]
private User user
{
set { User = value; }
}
[JsonProperty(@"replay_data")]
private Replay replay
{
set { Replay = value; }
}
[JsonProperty(@"score_id")]
private long onlineScoreID
{
set { OnlineScoreID = value; }
}
[JsonProperty(@"created_at")]
private DateTimeOffset date
{
set { Date = value; }
}
[JsonProperty(@"statistics")]
private Dictionary<string, dynamic> jsonStats
{
set
{
foreach (var kvp in value)
{
string key = kvp.Key;
switch (key)
{
case @"count_300":
key = @"300";
break;
case @"count_100":
key = @"100";
break;
case @"count_50":
key = @"50";
break;
case @"count_miss":
key = @"x";
break;
default:
continue;
}
Statistics.Add(key, kvp.Value);
}
}
}
[JsonProperty(@"mods")]
private string[] modStrings { get; set; }
public void ApplyBeatmap(BeatmapInfo beatmap)
{
Beatmap = beatmap;
Ruleset = beatmap.Ruleset;
// Evaluate the mod string
Mods = Ruleset.CreateInstance().GetAllMods().Where(mod => modStrings.Contains(mod.ShortenedName)).ToArray();
}
}
}

View File

@ -245,7 +245,11 @@ namespace osu.Game
LoadComponentAsync(Toolbar = new Toolbar
{
Depth = -4,
OnHome = delegate { intro?.ChildScreen?.MakeCurrent(); },
OnHome = delegate
{
hideAllOverlays();
intro?.ChildScreen?.MakeCurrent();
},
}, overlayContent.Add);
settings.StateChanged += delegate
@ -310,6 +314,16 @@ namespace osu.Game
private OsuScreen currentScreen;
private FrameworkConfigManager frameworkConfig;
private void hideAllOverlays()
{
settings.State = Visibility.Hidden;
chat.State = Visibility.Hidden;
direct.State = Visibility.Hidden;
social.State = Visibility.Hidden;
userProfile.State = Visibility.Hidden;
notificationOverlay.State = Visibility.Hidden;
}
private void screenChanged(Screen newScreen)
{
currentScreen = newScreen as OsuScreen;
@ -323,19 +337,12 @@ namespace osu.Game
//central game screen change logic.
if (!currentScreen.ShowOverlays)
{
settings.State = Visibility.Hidden;
Toolbar.State = Visibility.Hidden;
hideAllOverlays();
musicController.State = Visibility.Hidden;
chat.State = Visibility.Hidden;
direct.State = Visibility.Hidden;
social.State = Visibility.Hidden;
userProfile.State = Visibility.Hidden;
notificationOverlay.State = Visibility.Hidden;
Toolbar.State = Visibility.Hidden;
}
else
{
Toolbar.State = Visibility.Visible;
}
ScreenChanged?.Invoke(newScreen);
}

View File

@ -106,9 +106,15 @@ namespace osu.Game
connection.CreateTable<StoreVersion>();
dependencies.Cache(API = new APIAccess
{
Username = LocalConfig.Get<string>(OsuSetting.Username),
Token = LocalConfig.Get<string>(OsuSetting.Token)
});
dependencies.Cache(RulesetStore = new RulesetStore(connection));
dependencies.Cache(FileStore = new FileStore(connection, Host.Storage));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, Host));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, API, Host));
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, connection, Host, BeatmapManager, RulesetStore));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(connection, RulesetStore));
dependencies.Cache(new OsuColour());
@ -144,12 +150,6 @@ namespace osu.Game
Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap);
BeatmapManager.DefaultBeatmap = defaultBeatmap;
dependencies.Cache(API = new APIAccess
{
Username = LocalConfig.Get<string>(OsuSetting.Username),
Token = LocalConfig.Get<string>(OsuSetting.Token)
});
Beatmap.ValueChanged += b =>
{
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)

View File

@ -76,7 +76,6 @@ namespace osu.Game.Overlays.Chat
Size = new Vector2(text_size),
Shadow = false,
Margin = new MarginPadding { Right = 10f },
Alpha = 0f,
},
},
},
@ -109,7 +108,6 @@ namespace osu.Game.Overlays.Chat
TextSize = text_size,
Font = @"Exo2.0-SemiBold",
Shadow = false,
Alpha = 0.8f,
},
},
},
@ -151,6 +149,9 @@ namespace osu.Game.Overlays.Chat
joinedBind.ValueChanged += updateColour;
joinedBind.BindTo(channel.Joined);
joinedBind.TriggerChange();
FinishTransforms(true);
}
protected override bool OnHover(InputState state)

View File

@ -4,8 +4,6 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using System.IO;
using System.Threading.Tasks;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -20,8 +18,8 @@ using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Framework.Logging;
using osu.Game.Beatmaps.IO;
using osu.Game.Overlays.Notifications;
using osu.Game.Online.API.Requests;
namespace osu.Game.Overlays.Direct
{
@ -97,6 +95,11 @@ namespace osu.Game.Overlays.Direct
},
}
});
var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
if (downloadRequest != null)
attachDownload(downloadRequest);
}
protected override bool OnHover(InputState state)
@ -115,23 +118,8 @@ namespace osu.Game.Overlays.Direct
base.OnHoverLost(state);
}
// this should eventually be moved to a more central place, like BeatmapManager.
private DownloadBeatmapSetRequest downloadRequest;
protected void StartDownload()
{
if (api == null) return;
// we already have an active download running.
if (downloadRequest != null)
{
content.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine).Then();
return;
}
if (!api.LocalUser.Value.IsSupporter)
{
notifications.Post(new SimpleNotification
@ -142,72 +130,43 @@ namespace osu.Game.Overlays.Direct
return;
}
if (beatmaps.GetExistingDownload(SetInfo) != null)
{
// we already have an active download running.
content.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine).Then();
return;
}
var request = beatmaps.Download(SetInfo);
attachDownload(request);
}
private void attachDownload(DownloadBeatmapSetRequest request)
{
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
progressBar.Current.Value = 0;
ProgressNotification downloadNotification = new ProgressNotification
{
Text = $"Downloading {SetInfo.Metadata.Artist} - {SetInfo.Metadata.Title}",
};
downloadRequest = new DownloadBeatmapSetRequest(SetInfo);
downloadRequest.Failure += e =>
request.Failure += e =>
{
progressBar.Current.Value = 0;
progressBar.FadeOut(500);
downloadNotification.State = ProgressNotificationState.Completed;
Logger.Error(e, "Failed to get beatmap download information");
downloadRequest = null;
};
downloadRequest.Progress += (current, total) =>
{
float progress = (float)current / total;
request.DownloadProgressed += progress => progressBar.Current.Value = progress;
progressBar.Current.Value = progress;
downloadNotification.State = ProgressNotificationState.Active;
downloadNotification.Progress = progress;
};
downloadRequest.Success += data =>
request.Success += data =>
{
progressBar.Current.Value = 1;
progressBar.FadeOut(500);
downloadNotification.State = ProgressNotificationState.Completed;
using (var stream = new MemoryStream(data))
using (var archive = new OszArchiveReader(stream))
beatmaps.Import(archive);
};
downloadNotification.CancelRequested += () =>
{
downloadRequest.Cancel();
downloadRequest = null;
return true;
};
notifications.Post(downloadNotification);
// don't run in the main api queue as this is a long-running task.
Task.Run(() => downloadRequest.Perform(api));
}
public class DownloadBeatmapSetRequest : APIDownloadRequest
{
private readonly BeatmapSetInfo beatmapSet;
public DownloadBeatmapSetRequest(BeatmapSetInfo beatmapSet)
{
this.beatmapSet = beatmapSet;
}
protected override string Target => $@"beatmapsets/{beatmapSet.OnlineBeatmapSetID}/download";
}
protected override void LoadComplete()

View File

@ -27,6 +27,7 @@ namespace osu.Game.Overlays
private APIAccess api;
private RulesetStore rulesets;
private BeatmapManager beatmaps;
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
@ -46,9 +47,26 @@ namespace osu.Game.Overlays
set
{
if (beatmapSets?.Equals(value) ?? false) return;
beatmapSets = value;
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
if (beatmapSets == null) return;
var artists = new List<string>();
var songs = new List<string>();
var tags = new List<string>();
foreach (var s in beatmapSets)
{
artists.Add(s.Metadata.Artist);
songs.Add(s.Metadata.Title);
tags.AddRange(s.Metadata.Tags.Split(' '));
}
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
if (beatmapSets.Any() && panels == null)
// real use case? currently only seems to be for test case
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
}
}
@ -147,6 +165,8 @@ namespace osu.Game.Overlays
{
this.api = api;
this.rulesets = rulesets;
this.beatmaps = beatmaps;
resultCountsContainer.Colour = colours.Yellow;
beatmaps.BeatmapSetAdded += setAdded;
@ -156,6 +176,7 @@ namespace osu.Game.Overlays
{
// if a new map was imported, we should remove it from search results (download completed etc.)
panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire();
BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID);
}
private void updateResultCounts()
@ -237,20 +258,11 @@ namespace osu.Game.Overlays
getSetsRequest.Success += r =>
{
BeatmapSets = r?.Select(response => response.ToBeatmapSet(rulesets));
if (BeatmapSets == null) return;
BeatmapSets = r?.
Select(response => response.ToBeatmapSet(rulesets)).
Where(b => beatmaps.QueryBeatmapSet(q => q.OnlineBeatmapSetID == b.OnlineBeatmapSetID) == null);
var artists = new List<string>();
var songs = new List<string>();
var tags = new List<string>();
foreach (var s in BeatmapSets)
{
artists.Add(s.Metadata.Artist);
songs.Add(s.Metadata.Title);
tags.AddRange(s.Metadata.Tags.Split(' '));
}
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
};
api.Queue(getSetsRequest);

View File

@ -302,8 +302,8 @@ namespace osu.Game.Overlays
else
{
//figure out the best direction based on order in playlist.
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count();
var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo.ID).Count();
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count();
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
}

View File

@ -16,6 +16,11 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
public abstract string Name { get; }
/// <summary>
/// The shortened name of this mod.
/// </summary>
public abstract string ShortenedName { get; }
/// <summary>
/// The icon of this mod.
/// </summary>

View File

@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Mods
public class ModAutoplay : Mod
{
public override string Name => "Autoplay";
public override string ShortenedName => "AT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto;
public override string Description => "Watch a perfect automated play through the song";
public override double ScoreMultiplier => 0;

View File

@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mods
public class ModCinema : ModAutoplay
{
public override string Name => "Cinema";
public override string ShortenedName => "CN";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema;
}
}

View File

@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModDaycore : ModHalfTime
{
public override string Name => "Daycore";
public override string ShortenedName => "DC";
public override FontAwesome Icon => FontAwesome.fa_question;
public override string Description => "whoaaaaa";

View File

@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public class ModDoubleTime : Mod, IApplicableToClock
{
public override string Name => "Double Time";
public override string ShortenedName => "DT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Zoooooooooom";

View File

@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModEasy : Mod, IApplicableToDifficulty
{
public override string Name => "Easy";
public override string ShortenedName => "EZ";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required.";

View File

@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModFlashlight : Mod
{
public override string Name => "Flashlight";
public override string ShortenedName => "FL";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_flashlight;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Restricted view area.";

View File

@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModHalfTime : Mod, IApplicableToClock
{
public override string Name => "Half Time";
public override string ShortenedName => "HT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "Less zoom";

View File

@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModHardRock : Mod, IApplicableToDifficulty
{
public override string Name => "Hard Rock";
public override string ShortenedName => "HR";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Everything just got a bit harder...";

View File

@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModHidden : Mod
{
public override string Name => "Hidden";
public override string ShortenedName => "HD";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => true;

View File

@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModNightcore : ModDoubleTime
{
public override string Name => "Nightcore";
public override string ShortenedName => "NC";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore;
public override string Description => "uguuuuuuuu";

View File

@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModNoFail : Mod
{
public override string Name => "NoFail";
public override string ShortenedName => "NF";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "You can't fail, no matter what.";

View File

@ -6,6 +6,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModPerfect : ModSuddenDeath
{
public override string Name => "Perfect";
public override string ShortenedName => "PF";
public override string Description => "SS or quit.";
}
}

View File

@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModRelax : Mod
{
public override string Name => "Relax";
public override string ShortenedName => "RX";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax;
public override double ScoreMultiplier => 0;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) };

View File

@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModSuddenDeath : Mod
{
public override string Name => "Sudden Death";
public override string ShortenedName => "SD";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_suddendeath;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Miss a note and fail.";

View File

@ -6,6 +6,7 @@ namespace osu.Game.Rulesets.Mods
public class MultiMod : Mod
{
public override string Name => string.Empty;
public override string ShortenedName => string.Empty;
public override string Description => string.Empty;
public override double ScoreMultiplier => 0.0;

View File

@ -1,15 +1,17 @@
// 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.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Scoring;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets
{
@ -19,9 +21,18 @@ namespace osu.Game.Rulesets
public virtual IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { };
public IEnumerable<Mod> GetAllMods() => Enum.GetValues(typeof(ModType)).Cast<ModType>()
// Get all mod types as an IEnumerable<ModType>
.SelectMany(GetModsFor)
// Confine all mods of each mod type into a single IEnumerable<Mod>
.Where(mod => mod != null)
// Filter out all null mods
.SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod });
// Resolve MultiMods as their .Mods property
public abstract IEnumerable<Mod> GetModsFor(ModType type);
public abstract Mod GetAutoplayMod();
public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay);
protected Ruleset(RulesetInfo rulesetInfo)
{

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Users;
@ -15,70 +14,30 @@ namespace osu.Game.Rulesets.Scoring
{
public ScoreRank Rank { get; set; }
[JsonProperty(@"score")]
public double TotalScore { get; set; }
public double Accuracy { get; set; }
public double Health { get; set; } = 1;
[JsonProperty(@"max_combo")]
public int MaxCombo { get; set; }
public int Combo { get; set; }
[JsonProperty(@"mods")]
protected string[] ModStrings { get; set; } //todo: parse to Mod objects
public RulesetInfo Ruleset { get; set; }
public Mod[] Mods { get; set; }
[JsonProperty(@"user")]
public User User;
[JsonProperty(@"replay_data")]
public Replay Replay;
public BeatmapInfo Beatmap;
[JsonProperty(@"score_id")]
public long OnlineScoreID;
[JsonProperty(@"created_at")]
public DateTimeOffset Date;
[JsonProperty(@"statistics")]
private Dictionary<string, dynamic> jsonStats
{
set
{
foreach (var kvp in value)
{
string key = kvp.Key;
switch (key)
{
case @"count_300":
key = @"300";
break;
case @"count_100":
key = @"100";
break;
case @"count_50":
key = @"50";
break;
case @"count_miss":
key = @"x";
break;
default:
continue;
}
Statistics.Add(key, kvp.Value);
}
}
}
public Dictionary<string, dynamic> Statistics = new Dictionary<string, dynamic>();
}
}

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.UI
private readonly SpriteIcon modIcon;
private readonly SpriteIcon background;
private const float icon_size = 80;
private const float background_size = 80;
public FontAwesome Icon
{
@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.UI
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(icon_size),
Size = new Vector2(background_size),
Icon = FontAwesome.fa_osu_mod_bg,
Shadow = true,
},
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.UI
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Colour = OsuColour.Gray(84),
Size = new Vector2(icon_size - 35),
Size = new Vector2(background_size - 35),
Y = 25,
Icon = mod.Icon
},

View File

@ -28,6 +28,8 @@ namespace osu.Game.Screens.Play
public override void Add(KeyCounter key)
{
if (key == null) throw new ArgumentNullException(nameof(key));
base.Add(key);
key.IsCounting = IsCounting;
key.FadeTime = FadeTime;

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
@ -10,6 +11,8 @@ namespace osu.Game.Screens.Select
{
public class BeatmapDetailArea : Container
{
private const float details_padding = 10;
private readonly Container content;
protected override Container<Drawable> Content => content;
@ -66,9 +69,8 @@ namespace osu.Game.Screens.Select
Details = new BeatmapDetails
{
RelativeSizeAxes = Axes.X,
Masking = true,
Height = 352,
Alpha = 0,
Margin = new MarginPadding { Top = details_padding },
},
Leaderboard = new Leaderboard
{
@ -76,5 +78,12 @@ namespace osu.Game.Screens.Select
}
});
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Details.Height = Math.Min(DrawHeight - details_padding * 3 - BeatmapDetailAreaTabControl.HEIGHT, 450);
}
}
}

View File

@ -9,71 +9,189 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using System.Globalization;
using System.Linq;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Framework.Threading;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Screens.Select.Details;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Select
{
public class BeatmapDetails : Container
{
private readonly MetadataSegment description;
private readonly MetadataSegment source;
private readonly MetadataSegment tags;
private const float spacing = 10;
private const float transition_duration = 250;
private readonly DifficultyRow circleSize;
private readonly DifficultyRow drainRate;
private readonly DifficultyRow overallDifficulty;
private readonly DifficultyRow approachRate;
private readonly DifficultyRow stars;
private readonly FillFlowContainer top, statsFlow;
private readonly AdvancedStats advanced;
private readonly DetailBox ratingsContainer;
private readonly UserRatings ratings;
private readonly ScrollContainer metadataScroll;
private readonly MetadataSection description, source, tags;
private readonly Container failRetryContainer;
private readonly FailRetryGraph failRetryGraph;
private readonly DimmedLoadingAnimation loading;
private readonly Container ratingsContainer;
private readonly Bar ratingsBar;
private readonly OsuSpriteText negativeRatings;
private readonly OsuSpriteText positiveRatings;
private readonly BarGraph ratingsGraph;
private readonly FillFlowContainer retryFailContainer;
private readonly BarGraph retryGraph;
private readonly BarGraph failGraph;
private APIAccess api;
private ScheduledDelegate pendingBeatmapSwitch;
private BeatmapInfo beatmap;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (beatmap == value) return;
if (value == beatmap) return;
beatmap = value;
pendingBeatmapSwitch?.Cancel();
pendingBeatmapSwitch = Schedule(updateStats);
pendingBeatmapSwitch = Schedule(updateStatistics);
}
}
private void updateStats()
public BeatmapDetails()
{
if (beatmap == null) return;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = spacing },
Children = new Drawable[]
{
top = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
statsFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.5f,
Spacing = new Vector2(spacing),
Padding = new MarginPadding { Right = spacing / 2 },
Children = new[]
{
new DetailBox
{
Child = advanced = new AdvancedStats
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = spacing, Top = spacing * 2, Bottom = spacing },
},
},
ratingsContainer = new DetailBox
{
Child = ratings = new UserRatings
{
RelativeSizeAxes = Axes.X,
Height = 134,
Padding = new MarginPadding { Horizontal = spacing, Top = spacing },
},
},
},
},
metadataScroll = new ScrollContainer
{
RelativeSizeAxes = Axes.X,
Width = 0.5f,
ScrollbarVisible = false,
Padding = new MarginPadding { Left = spacing / 2 },
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
LayoutDuration = transition_duration,
Spacing = new Vector2(spacing * 2),
Margin = new MarginPadding { Top = spacing * 2 },
Children = new[]
{
description = new MetadataSection("Description")
{
TextColour = Color4.White.Opacity(0.75f),
},
source = new MetadataSection("Source")
{
TextColour = Color4.White.Opacity(0.75f),
},
tags = new MetadataSection("Tags"),
},
},
},
},
},
failRetryContainer = new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Points of Failure",
Font = @"Exo2.0-Bold",
TextSize = 14,
},
failRetryGraph = new FailRetryGraph
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 14 + spacing / 2 },
},
},
},
},
},
loading = new DimmedLoadingAnimation
{
RelativeSizeAxes = Axes.Both,
},
};
}
description.Text = beatmap.Version;
source.Text = beatmap.Metadata.Source;
tags.Text = beatmap.Metadata.Tags;
[BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api)
{
this.api = api;
tags.TextColour = colours.Yellow;
}
circleSize.Value = beatmap.Difficulty.CircleSize;
drainRate.Value = beatmap.Difficulty.DrainRate;
overallDifficulty.Value = beatmap.Difficulty.OverallDifficulty;
approachRate.Value = beatmap.Difficulty.ApproachRate;
stars.Value = (float)beatmap.StarDifficulty;
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
var requestedBeatmap = beatmap;
metadataScroll.Height = statsFlow.DrawHeight;
failRetryContainer.Height = DrawHeight - Padding.TotalVertical - (top.DrawHeight + spacing / 2);
}
private void updateStatistics()
{
if (Beatmap == null)
{
clearStats();
return;
}
ratingsContainer.FadeIn(transition_duration);
advanced.Beatmap = Beatmap;
description.Text = Beatmap.Version;
source.Text = Beatmap.Metadata.Source;
tags.Text = Beatmap.Metadata.Tags;
var requestedBeatmap = Beatmap;
if (requestedBeatmap.Metrics == null)
{
var lookup = new GetBeatmapDetailsRequest(requestedBeatmap);
@ -84,413 +202,195 @@ namespace osu.Game.Screens.Select
return;
requestedBeatmap.Metrics = res;
Schedule(() => updateMetrics(res));
Schedule(() => displayMetrics(res));
};
lookup.Failure += e => Schedule(() => updateMetrics(null));
lookup.Failure += e => Schedule(() => displayMetrics(null));
api.Queue(lookup);
loading.Show();
}
updateMetrics(requestedBeatmap.Metrics, false);
displayMetrics(requestedBeatmap.Metrics, false);
}
/// <summary>
/// Update displayed metrics.
/// </summary>
/// <param name="metrics">New metrics to overwrite the existing display. Can be null.</param>
/// <param name="failOnMissing">Whether to hide the display on null or empty metrics. If false, we will dim as if waiting for further updates.</param>
private void updateMetrics(BeatmapMetrics metrics, bool failOnMissing = true)
private void displayMetrics(BeatmapMetrics metrics, bool failOnMissing = true)
{
var hasRatings = metrics?.Ratings.Any() ?? false;
var hasRetriesFails = (metrics?.Retries.Any() ?? false) && metrics.Fails.Any();
var hasRatings = metrics?.Ratings?.Any() ?? false;
var hasRetriesFails = (metrics?.Retries?.Any() ?? false) && (metrics.Fails?.Any() ?? false);
if (failOnMissing)
loading.Hide();
if (failOnMissing) loading.Hide();
if (hasRatings)
{
var ratings = metrics.Ratings.ToList();
ratingsContainer.Show();
negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
ratingsGraph.Values = ratings.Select(rating => (float)rating);
ratingsContainer.FadeColour(Color4.White, 500, Easing.Out);
ratings.Metrics = metrics;
ratings.FadeIn(transition_duration);
}
else if (failOnMissing)
ratingsGraph.Values = new float[10];
{
ratings.Metrics = new BeatmapMetrics
{
Ratings = new int[10],
};
}
else
ratingsContainer.FadeColour(Color4.Gray, 500, Easing.Out);
{
ratings.FadeTo(0.25f, transition_duration);
}
if (hasRetriesFails)
{
var retries = metrics.Retries;
var fails = metrics.Fails;
retryFailContainer.Show();
float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
failGraph.MaxValue = maxValue;
retryGraph.MaxValue = maxValue;
failGraph.Values = fails.Select(fail => (float)fail);
retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
retryFailContainer.FadeColour(Color4.White, 500, Easing.Out);
failRetryGraph.Metrics = metrics;
failRetryContainer.FadeIn(transition_duration);
}
else if (failOnMissing)
{
failGraph.Values = new float[100];
retryGraph.Values = new float[100];
failRetryGraph.Metrics = new BeatmapMetrics
{
Fails = new int[100],
Retries = new int[100],
};
}
else
retryFailContainer.FadeColour(Color4.Gray, 500, Easing.Out);
{
failRetryContainer.FadeTo(0.25f, transition_duration);
}
}
public BeatmapDetails()
private void clearStats()
{
Children = new Drawable[]
description.Text = null;
source.Text = null;
tags.Text = null;
advanced.Beatmap = new BeatmapInfo
{
new Box
StarDifficulty = 0,
Difficulty = new BeatmapDifficulty
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
CircleSize = 0,
DrainRate = 0,
OverallDifficulty = 0,
ApproachRate = 0,
},
new FillFlowContainer<MetadataSegment>
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.4f,
Direction = FillDirection.Vertical,
LayoutDuration = 200,
LayoutEasing = Easing.OutQuint,
Children = new[]
{
description = new MetadataSegment("Description"),
source = new MetadataSegment("Source"),
tags = new MetadataSegment("Tags")
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.6f,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 15),
Padding = new MarginPadding(10) { Top = 0 },
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Padding = new MarginPadding(10),
Children = new[]
{
circleSize = new DifficultyRow("Circle Size", 7),
drainRate = new DifficultyRow("HP Drain"),
overallDifficulty = new DifficultyRow("Accuracy"),
approachRate = new DifficultyRow("Approach Rate"),
stars = new DifficultyRow("Star Difficulty"),
},
},
},
},
ratingsContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Alpha = 0,
AlwaysPresent = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding
{
Top = 25,
Left = 15,
Right = 15,
},
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "User Rating",
Font = @"Exo2.0-Medium",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
ratingsBar = new Bar
{
RelativeSizeAxes = Axes.X,
Height = 5,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
negativeRatings = new OsuSpriteText
{
Font = @"Exo2.0-Regular",
Text = "0",
},
positiveRatings = new OsuSpriteText
{
Font = @"Exo2.0-Regular",
Text = "0",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
},
},
new OsuSpriteText
{
Text = "Rating Spread",
TextSize = 14,
Font = @"Exo2.0-Regular",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
ratingsGraph = new BarGraph
{
RelativeSizeAxes = Axes.X,
Height = 50,
},
},
},
},
},
retryFailContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Alpha = 0,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Points of Failure",
Font = @"Exo2.0-Regular",
},
new Container<BarGraph>
{
RelativeSizeAxes = Axes.X,
Size = new Vector2(1 / 0.6f, 50),
Children = new[]
{
retryGraph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
failGraph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
},
},
}
},
},
},
loading = new LoadingAnimation()
};
loading.Hide();
ratingsContainer.FadeOut(transition_duration);
failRetryContainer.FadeOut(transition_duration);
}
private APIAccess api;
private readonly LoadingAnimation loading;
[BackgroundDependencyLoader]
private void load(OsuColour colour, APIAccess api)
private class DetailBox : Container
{
this.api = api;
private readonly Container content;
protected override Container<Drawable> Content => content;
description.AccentColour = colour.GrayB;
source.AccentColour = colour.GrayB;
tags.AccentColour = colour.YellowLight;
stars.AccentColour = colour.Yellow;
ratingsBar.BackgroundColour = colour.Green;
ratingsBar.AccentColour = colour.YellowDark;
ratingsGraph.Colour = colour.BlueDark;
failGraph.Colour = colour.YellowDarker;
retryGraph.Colour = colour.Yellow;
}
private class DifficultyRow : Container, IHasAccentColour
{
private readonly OsuSpriteText name;
private readonly Bar bar;
private readonly OsuSpriteText valueText;
private readonly float maxValue;
private float difficultyValue;
public float Value
public DetailBox()
{
get
{
return difficultyValue;
}
set
{
difficultyValue = value;
bar.Length = value / maxValue;
valueText.Text = value.ToString("N1", CultureInfo.CurrentCulture);
}
}
public Color4 AccentColour
{
get
{
return bar.AccentColour;
}
set
{
bar.AccentColour = value;
}
}
public DifficultyRow(string difficultyName, float maxValue = 10)
{
this.maxValue = maxValue;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
name = new OsuSpriteText
new Box
{
Font = @"Exo2.0-Regular",
Text = difficultyName,
},
bar = new Bar
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(1, 0.35f),
Padding = new MarginPadding { Left = 100, Right = 25 },
Colour = Color4.Black.Opacity(0.5f),
},
valueText = new OsuSpriteText
content = new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = @"Exo2.0-Regular",
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
name.Colour = colour.GrayB;
bar.BackgroundColour = colour.Gray7;
valueText.Colour = colour.GrayB;
}
}
private class MetadataSegment : Container, IHasAccentColour
private class MetadataSection : Container
{
private readonly OsuSpriteText header;
private readonly FillFlowContainer<OsuSpriteText> content;
private readonly TextFlowContainer textFlow;
public string Text
{
set
{
if (string.IsNullOrEmpty(value))
Hide();
else
{
Show();
if (header.Text == "Tags")
content.ChildrenEnumerable = value.Split(' ').Select(text => new OsuSpriteText
{
Text = text,
Font = "Exo2.0-Regular",
});
else
content.Children = new[]
{
new OsuSpriteText
{
Text = value,
Font = "Exo2.0-Regular",
}
};
this.FadeOut(transition_duration);
return;
}
this.FadeIn(transition_duration);
textFlow.Clear();
textFlow.AddText(value, s => s.TextSize = 14);
}
}
public Color4 AccentColour
public Color4 TextColour
{
get
{
return content.Colour;
}
set
{
content.Colour = value;
}
get { return textFlow.Colour; }
set { textFlow.Colour = value; }
}
public MetadataSegment(string headerText)
public MetadataSection(string title)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Margin = new MarginPadding { Top = 10 };
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(spacing / 2),
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = new OsuSpriteText
{
Text = title,
Font = @"Exo2.0-Bold",
TextSize = 14,
},
},
textFlow = new TextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
},
};
}
}
private class DimmedLoadingAnimation : VisibilityContainer
{
private readonly LoadingAnimation loading;
public DimmedLoadingAnimation()
{
Children = new Drawable[]
{
header = new OsuSpriteText
new Box
{
Font = @"Exo2.0-Bold",
Text = headerText,
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
content = new FillFlowContainer<OsuSpriteText>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
Spacing = new Vector2(5, 0),
Margin = new MarginPadding { Top = header.TextSize }
}
loading = new LoadingAnimation(),
};
}
protected override void PopIn()
{
this.FadeIn(transition_duration, Easing.OutQuint);
loading.State = Visibility.Visible;
}
protected override void PopOut()
{
this.FadeOut(transition_duration, Easing.OutQuint);
loading.State = Visibility.Hidden;
}
}
}
}

View File

@ -0,0 +1,152 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using System;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Select.Details
{
public class AdvancedStats : Container
{
private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (value == beatmap) return;
beatmap = value;
//mania specific
if ((Beatmap?.Ruleset?.ID ?? 0) == 3)
{
firstValue.Title = "Key Amount";
firstValue.Value = (int)Math.Round(Beatmap?.Difficulty?.CircleSize ?? 0);
}
else
{
firstValue.Title = "Circle Size";
firstValue.Value = Beatmap?.Difficulty?.CircleSize ?? 0;
}
hpDrain.Value = beatmap.Difficulty.DrainRate;
accuracy.Value = beatmap.Difficulty.OverallDifficulty;
approachRate.Value = beatmap.Difficulty.ApproachRate;
starDifficulty.Value = (float)beatmap.StarDifficulty;
}
}
public AdvancedStats()
{
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(4f),
Children = new[]
{
firstValue = new StatisticRow(), //circle size/key amount
hpDrain = new StatisticRow { Title = "HP Drain" },
accuracy = new StatisticRow { Title = "Accuracy" },
approachRate = new StatisticRow { Title = "Approach Rate" },
starDifficulty = new StatisticRow(10, true) { Title = "Star Difficulty" },
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
starDifficulty.AccentColour = colours.Yellow;
}
private class StatisticRow : Container, IHasAccentColour
{
private const float value_width = 25;
private const float name_width = 70;
private readonly float maxValue;
private readonly bool forceDecimalPlaces;
private readonly OsuSpriteText name, value;
private readonly Bar bar;
public string Title
{
get { return name.Text; }
set { name.Text = value; }
}
private float difficultyValue;
public float Value
{
get { return difficultyValue; }
set
{
difficultyValue = value;
bar.Length = value / maxValue;
this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##");
}
}
public Color4 AccentColour
{
get { return bar.AccentColour; }
set { bar.AccentColour = value; }
}
public StatisticRow(float maxValue = 10, bool forceDecimalPlaces = false)
{
this.maxValue = maxValue;
this.forceDecimalPlaces = forceDecimalPlaces;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new Container
{
Width = name_width,
AutoSizeAxes = Axes.Y,
Child = name = new OsuSpriteText
{
TextSize = 13,
},
},
bar = new Bar
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 5,
BackgroundColour = Color4.White.Opacity(0.5f),
Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 },
},
new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Width = value_width,
RelativeSizeAxes = Axes.Y,
Child = value = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 13,
},
},
};
}
}
}
}

View File

@ -0,0 +1,62 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using System.Linq;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Select.Details
{
public class FailRetryGraph : Container
{
private readonly BarGraph retryGraph, failGraph;
private BeatmapMetrics metrics;
public BeatmapMetrics Metrics
{
get { return metrics; }
set
{
if (value == metrics) return;
metrics = value;
var retries = Metrics.Retries;
var fails = Metrics.Fails;
float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
failGraph.MaxValue = maxValue;
retryGraph.MaxValue = maxValue;
failGraph.Values = fails.Select(f => (float)f);
retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
}
}
public FailRetryGraph()
{
Children = new[]
{
retryGraph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
failGraph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
retryGraph.Colour = colours.Yellow;
failGraph.Colour = colours.YellowDarker;
}
}
}

View File

@ -0,0 +1,122 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using System.Linq;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Select.Details
{
public class UserRatings : Container
{
private readonly FillFlowContainer header;
private readonly Bar ratingsBar;
private readonly OsuSpriteText negativeRatings, positiveRatings;
private readonly Container graphContainer;
private readonly BarGraph graph;
private BeatmapMetrics metrics;
public BeatmapMetrics Metrics
{
get { return metrics; }
set
{
if (value == metrics) return;
metrics = value;
var ratings = Metrics.Ratings.ToList();
negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
graph.Values = Metrics.Ratings.Select(r => (float)r);
}
}
public UserRatings()
{
Children = new Drawable[]
{
header = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "User Rating",
TextSize = 13,
},
ratingsBar = new Bar
{
RelativeSizeAxes = Axes.X,
Height = 5,
Margin = new MarginPadding { Top = 5 },
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
negativeRatings = new OsuSpriteText
{
Text = "0",
TextSize = 13,
},
positiveRatings = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Text = @"0",
TextSize = 13,
},
},
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Rating Spread",
TextSize = 13,
Margin = new MarginPadding { Top = 10, Bottom = 5 },
},
},
},
graphContainer = new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Child = graph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ratingsBar.BackgroundColour = colours.Green;
ratingsBar.AccentColour = colours.Yellow;
graph.Colour = colours.BlueDark;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
graphContainer.Padding = new MarginPadding { Top = header.DrawHeight };
}
}
}

View File

@ -1,15 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using System;
using osu.Framework.Allocation;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;

View File

@ -4,17 +4,19 @@
using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Rulesets.Mods;
using osu.Game.Users;
using osu.Framework;
using osu.Game.Rulesets.Scoring;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Users;
namespace osu.Game.Screens.Select.Leaderboards
{
@ -41,7 +43,7 @@ namespace osu.Game.Screens.Select.Leaderboards
private readonly ScoreComponentLabel maxCombo;
private readonly ScoreComponentLabel accuracy;
private readonly Container flagBadgeContainer;
private readonly FillFlowContainer<ScoreModIcon> modsContainer;
private readonly FillFlowContainer<ModIcon> modsContainer;
private Visibility state;
@ -247,7 +249,7 @@ namespace osu.Game.Screens.Select.Leaderboards
},
},
},
modsContainer = new FillFlowContainer<ScoreModIcon>
modsContainer = new FillFlowContainer<ModIcon>
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
@ -260,13 +262,13 @@ namespace osu.Game.Screens.Select.Leaderboards
},
};
if (Score.Mods != null)
foreach (Mod mod in Score.Mods)
{
foreach (Mod mod in Score.Mods)
modsContainer.Add(new ModIcon(mod)
{
// TODO: Get actual mod colours
modsContainer.Add(new ScoreModIcon(mod.Icon, OsuColour.FromHex(@"ffcc22")));
}
AutoSizeAxes = Axes.Both,
Scale = new Vector2(0.375f)
});
}
}
@ -275,13 +277,13 @@ namespace osu.Game.Screens.Select.Leaderboards
public override void Hide() => State = Visibility.Hidden;
public override void Show() => State = Visibility.Visible;
protected override bool OnHover(Framework.Input.InputState state)
protected override bool OnHover(InputState state)
{
background.FadeTo(0.5f, 300, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(Framework.Input.InputState state)
protected override void OnHoverLost(InputState state)
{
background.FadeTo(background_alpha, 200, Easing.OutQuint);
base.OnHoverLost(state);
@ -311,8 +313,8 @@ namespace osu.Game.Screens.Select.Leaderboards
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = font,
TextSize = textSize,
FixedWidth = true,
TextSize = textSize,
Text = text,
Colour = glowColour,
Shadow = false,
@ -334,36 +336,6 @@ namespace osu.Game.Screens.Select.Leaderboards
}
}
private class ScoreModIcon : Container
{
public ScoreModIcon(FontAwesome icon, Color4 colour)
{
AutoSizeAxes = Axes.Both;
Children = new[]
{
new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Icon = FontAwesome.fa_osu_mod_bg,
Colour = colour,
Shadow = true,
Size = new Vector2(30),
},
new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Icon = icon,
Colour = OsuColour.Gray(84),
Size = new Vector2(18),
Position = new Vector2(0f, 2f),
},
};
}
}
private class ScoreComponentLabel : Container
{
public ScoreComponentLabel(FontAwesome icon, string value)

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game</RootNamespace>
<AssemblyName>osu.Game</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
@ -98,6 +98,7 @@
<Compile Include="Input\Bindings\GlobalKeyBindingInputManager.cs" />
<Compile Include="IO\FileStore.cs" />
<Compile Include="IO\FileInfo.cs" />
<Compile Include="Online\API\Requests\DownloadBeatmapSetRequest.cs" />
<Compile Include="Online\API\Requests\GetUsersRequest.cs" />
<Compile Include="Online\API\Requests\PostMessageRequest.cs" />
<Compile Include="Online\Chat\ErrorMessage.cs" />
@ -541,6 +542,9 @@
<Compile Include="Overlays\OnlineBeatmapSetOverlay.cs" />
<Compile Include="Overlays\OnlineBeatmapSet\Info.cs" />
<Compile Include="Overlays\OnlineBeatmapSet\Header.cs" />
<Compile Include="Screens\Select\Details\AdvancedStats.cs" />
<Compile Include="Screens\Select\Details\FailRetryGraph.cs" />
<Compile Include="Screens\Select\Details\UserRatings.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
@ -566,4 +570,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>