1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 11:27:24 +08:00

Merge branch 'master' of git://github.com/ppy/osu into direct-previews

This commit is contained in:
Jorolf 2017-09-26 12:22:11 +02:00
commit 355a7b6649
54 changed files with 3977 additions and 100 deletions

@ -1 +1 @@
Subproject commit e1352a8b0b5d1ba8acd9335a56c714d2ccc2f6a6
Subproject commit 5f3a7fe4d0537820a33b817a41623b4b22a3ec59

View File

@ -29,7 +29,7 @@ namespace osu.Desktop.Deploy
public static string SolutionName = ConfigurationManager.AppSettings["SolutionName"];
public static string ProjectName = ConfigurationManager.AppSettings["ProjectName"];
public static string NuSpecName = ConfigurationManager.AppSettings["NuSpecName"];
public static string TargetName = ConfigurationManager.AppSettings["TargetName"];
public static string TargetNames = ConfigurationManager.AppSettings["TargetName"];
public static string PackageName = ConfigurationManager.AppSettings["PackageName"];
public static string IconName = ConfigurationManager.AppSettings["IconName"];
public static string CodeSigningCertificate = ConfigurationManager.AppSettings["CodeSigningCertificate"];
@ -100,7 +100,8 @@ namespace osu.Desktop.Deploy
updateAssemblyInfo(version);
write("Running build process...");
runCommand(msbuild_path, $"/v:quiet /m /t:{TargetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln");
foreach (string targetName in TargetNames.Split(','))
runCommand(msbuild_path, $"/v:quiet /m /t:{targetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln");
write("Creating NuGet deployment package...");
runCommand(nuget_path, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}");

View File

@ -0,0 +1,146 @@
// 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.IO;
using NUnit.Framework;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Beatmaps.Formats;
using osu.Game.Tests.Resources;
using System.Linq;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Tests.Beatmaps.Formats
{
[TestFixture]
public class OsuLegacyDecoderTest
{
[Test]
public void TestDecodeMetadata()
{
var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{
var beatmap = decoder.Decode(new StreamReader(stream));
var meta = beatmap.BeatmapInfo.Metadata;
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
Assert.AreEqual("Soleily", meta.Artist);
Assert.AreEqual("Soleily", meta.ArtistUnicode);
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
Assert.AreEqual("Gamu", meta.Author);
Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile);
Assert.AreEqual(164471, meta.PreviewTime);
Assert.AreEqual(string.Empty, meta.Source);
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags);
Assert.AreEqual("Renatus", meta.Title);
Assert.AreEqual("Renatus", meta.TitleUnicode);
}
}
[Test]
public void TestDecodeGeneral()
{
var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{
var beatmapInfo = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
Assert.AreEqual(false, beatmapInfo.Countdown);
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
Assert.AreEqual(false, beatmapInfo.SpecialStyle);
Assert.IsTrue(beatmapInfo.RulesetID == 0);
Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
}
}
[Test]
public void TestDecodeEditor()
{
var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{
var beatmap = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
int[] expectedBookmarks =
{
11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351,
95901, 106450, 116999, 119637, 130186, 140735, 151285,
161834, 164471, 175020, 185570, 196119, 206669, 209306
};
Assert.AreEqual(expectedBookmarks.Length, beatmap.Bookmarks.Length);
for (int i = 0; i < expectedBookmarks.Length; i++)
Assert.AreEqual(expectedBookmarks[i], beatmap.Bookmarks[i]);
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
Assert.AreEqual(4, beatmap.BeatDivisor);
Assert.AreEqual(4, beatmap.GridSize);
Assert.AreEqual(2, beatmap.TimelineZoom);
}
}
[Test]
public void TestDecodeDifficulty()
{
var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{
var beatmap = decoder.Decode(new StreamReader(stream));
var difficulty = beatmap.BeatmapInfo.Difficulty;
Assert.AreEqual(6.5f, difficulty.DrainRate);
Assert.AreEqual(4, difficulty.CircleSize);
Assert.AreEqual(8, difficulty.OverallDifficulty);
Assert.AreEqual(9, difficulty.ApproachRate);
Assert.AreEqual(1.8f, difficulty.SliderMultiplier);
Assert.AreEqual(2, difficulty.SliderTickRate);
}
}
[Test]
public void TestDecodeColors()
{
var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{
var beatmap = decoder.Decode(new StreamReader(stream));
Color4[] expected =
{
new Color4(142, 199, 255, 255),
new Color4(255, 128, 128, 255),
new Color4(128, 255, 255, 255),
new Color4(128, 255, 128, 255),
new Color4(255, 187, 255, 255),
new Color4(255, 177, 140, 255),
};
Assert.AreEqual(expected.Length, beatmap.ComboColors.Count);
for (int i = 0; i < expected.Length; i++)
Assert.AreEqual(expected[i], beatmap.ComboColors[i]);
}
}
[Test]
public void TestDecodeHitObjects()
{
var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{
var beatmap = decoder.Decode(new StreamReader(stream));
var curveData = beatmap.HitObjects[0] as IHasCurve;
var positionData = beatmap.HitObjects[0] as IHasPosition;
Assert.IsNotNull(positionData);
Assert.IsNotNull(curveData);
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
Assert.AreEqual(956, beatmap.HitObjects[0].StartTime);
Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
positionData = beatmap.HitObjects[1] as IHasPosition;
Assert.IsNotNull(positionData);
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime);
Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
}
}
}
}

View File

@ -0,0 +1,164 @@
// 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.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Platform;
using osu.Game.IPC;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
namespace osu.Game.Tests.Beatmaps.IO
{
[TestFixture]
public class ImportBeatmapTest
{
private const string osz_path = @"../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
[Test]
public void TestImportWhenClosed()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new HeadlessGameHost())
{
var osu = loadOsu(host);
var temp = prepareTempCopy(osz_path);
Assert.IsTrue(File.Exists(temp));
osu.Dependencies.Get<BeatmapManager>().Import(temp);
ensureLoaded(osu);
Assert.IsFalse(File.Exists(temp));
}
}
[Test]
public void TestImportOverIPC()
{
using (HeadlessGameHost host = new HeadlessGameHost("host", true))
using (HeadlessGameHost client = new HeadlessGameHost("client", true))
{
Assert.IsTrue(host.IsPrimaryInstance);
Assert.IsTrue(!client.IsPrimaryInstance);
var osu = loadOsu(host);
var temp = prepareTempCopy(osz_path);
Assert.IsTrue(File.Exists(temp));
var importer = new BeatmapIPCChannel(client);
if (!importer.ImportAsync(temp).Wait(10000))
Assert.Fail(@"IPC took too long to send");
ensureLoaded(osu);
Assert.IsFalse(File.Exists(temp));
}
}
[Test]
public void TestImportWhenFileOpen()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new HeadlessGameHost())
{
var osu = loadOsu(host);
var temp = prepareTempCopy(osz_path);
Assert.IsTrue(File.Exists(temp), "Temporary file copy never substantiated");
using (File.OpenRead(temp))
osu.Dependencies.Get<BeatmapManager>().Import(temp);
ensureLoaded(osu);
File.Delete(temp);
Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't");
}
}
private string prepareTempCopy(string path)
{
var temp = Path.GetTempFileName();
return new FileInfo(path).CopyTo(temp, true).FullName;
}
private OsuGameBase loadOsu(GameHost host)
{
host.Storage.DeleteDatabase(@"client");
var osu = new OsuGameBase();
Task.Run(() => host.Run(osu));
while (!osu.IsLoaded)
Thread.Sleep(1);
return osu;
}
private void ensureLoaded(OsuGameBase osu, int timeout = 60000)
{
IEnumerable<BeatmapSetInfo> resultSets = null;
var store = osu.Dependencies.Get<BeatmapManager>();
Action waitAction = () =>
{
while (!(resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any())
Thread.Sleep(50);
};
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout),
@"BeatmapSet did not import to the database in allocated time.");
//ensure we were stored to beatmap database backing...
Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1).");
IEnumerable<BeatmapInfo> resultBeatmaps = null;
//if we don't re-check here, the set will be inserted but the beatmaps won't be present yet.
waitAction = () =>
{
while ((resultBeatmaps = store.QueryBeatmaps(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12)
Thread.Sleep(50);
};
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout),
@"Beatmaps did not import to the database in allocated time");
var set = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526).First();
Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count(),
$@"Incorrect database beatmap count post-import ({resultBeatmaps.Count()} but should be {set.Beatmaps.Count}).");
foreach (BeatmapInfo b in resultBeatmaps)
Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID));
Assert.IsTrue(set.Beatmaps.Count > 0);
var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
}
}
}

View File

@ -0,0 +1,83 @@
// 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.IO;
using System.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.IO;
using osu.Game.Tests.Resources;
using osu.Game.Beatmaps.Formats;
namespace osu.Game.Tests.Beatmaps.IO
{
[TestFixture]
public class OszArchiveReaderTest
{
[Test]
public void TestReadBeatmaps()
{
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
{
var reader = new OszArchiveReader(osz);
string[] expected =
{
"Soleily - Renatus (Deif) [Platter].osu",
"Soleily - Renatus (Deif) [Rain].osu",
"Soleily - Renatus (Deif) [Salad].osu",
"Soleily - Renatus (ExPew) [Another].osu",
"Soleily - Renatus (ExPew) [Hyper].osu",
"Soleily - Renatus (ExPew) [Normal].osu",
"Soleily - Renatus (Gamu) [Hard].osu",
"Soleily - Renatus (Gamu) [Insane].osu",
"Soleily - Renatus (Gamu) [Normal].osu",
"Soleily - Renatus (MMzz) [Futsuu].osu",
"Soleily - Renatus (MMzz) [Muzukashii].osu",
"Soleily - Renatus (MMzz) [Oni].osu"
};
var maps = reader.Filenames.ToArray();
foreach (var map in expected)
Assert.Contains(map, maps);
}
}
[Test]
public void TestReadMetadata()
{
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
{
var reader = new OszArchiveReader(osz);
BeatmapMetadata meta;
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
meta = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
Assert.AreEqual("Soleily", meta.Artist);
Assert.AreEqual("Soleily", meta.ArtistUnicode);
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
Assert.AreEqual("Deif", meta.Author);
Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile);
Assert.AreEqual(164471, meta.PreviewTime);
Assert.AreEqual(string.Empty, meta.Source);
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags);
Assert.AreEqual("Renatus", meta.Title);
Assert.AreEqual("Renatus", meta.TitleUnicode);
}
}
[Test]
public void TestReadFile()
{
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
{
var reader = new OszArchiveReader(osz);
using (var stream = new StreamReader(
reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
{
Assert.AreEqual("osu file format v13", stream.ReadLine()?.Trim());
}
}
}
}
}

View File

@ -0,0 +1,25 @@
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
<!-- XQuartz compatibility (X11 on Mac) -->
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>

View File

@ -0,0 +1,20 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.IO;
using System.Reflection;
namespace osu.Game.Tests.Resources
{
public static class Resource
{
public static Stream OpenResource(string name)
{
var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path));
return Assembly.GetExecutingAssembly().GetManifestResourceStream($@"osu.Game.Tests.Resources.{name}") ??
Assembly.LoadFrom(Path.Combine(localPath, @"osu.Game.Resources.dll")).GetManifestResourceStream($@"osu.Game.Resources.{name}");
}
}
}

File diff suppressed because it is too large Load Diff

11
osu.Game.Tests/app.config Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{54377672-20B1-40AF-8087-5CF73BF3953A}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>osu.Game.Tests</RootNamespace>
<AssemblyName>osu.Game.Tests</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="SQLite.Net">
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
</Reference>
<Reference Include="SQLite.Net.Platform.Win32">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath>
</Reference>
<Reference Include="SQLite.Net.Platform.Generic">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link>
</None>
<None Include="app.config" />
<None Include="packages.config" />
<None Include="OpenTK.dll.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
<Name>osu.Framework</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
<Project>{c92a607b-1fdd-4954-9f92-03ff547d9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj">
<Project>{58f6c80c-1253-4a0e-a465-b8c85ebeadf3}</Project>
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj">
<Project>{48f4582b-7687-4621-9cbe-5c24197cb536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{f167e17a-7de6-4af5-b920-a5112296c695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project>
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
<Project>{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}</Project>
<Name>osu.Game.Resources</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<Compile Include="Beatmaps\IO\OszArchiveReaderTest.cs" />
<Compile Include="Beatmaps\IO\ImportBeatmapTest.cs" />
<Compile Include="Resources\Resource.cs" />
<Compile Include="Beatmaps\Formats\OsuLegacyDecoderTest.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Soleily - Renatus %28Gamu%29 [Insane].osu" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<packages>
<package id="NUnit" version="3.8.1" targetFramework="net461" />
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
</packages>

View File

@ -10,6 +10,28 @@ namespace osu.Game.Beatmaps
/// </summary>
public class BeatmapOnlineInfo
{
/// <summary>
/// The length in milliseconds of this beatmap's song.
/// </summary>
public double Length { get; set; }
/// <summary>
/// Whether or not this beatmap has a background video.
/// </summary>
public bool HasVideo { get; set; }
/// <summary>
/// The amount of circles in this beatmap.
/// </summary>
[JsonProperty(@"count_circles")]
public int CircleCount { get; set; }
/// <summary>
/// The amount of sliders in this beatmap.
/// </summary>
[JsonProperty(@"count_sliders")]
public int SliderCount { get; set; }
/// <summary>
/// The amount of plays this beatmap has.
/// </summary>

View File

@ -1,7 +1,9 @@
// 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 Newtonsoft.Json;
using osu.Game.Users;
namespace osu.Game.Beatmaps
{
@ -10,6 +12,26 @@ namespace osu.Game.Beatmaps
/// </summary>
public class BeatmapSetOnlineInfo
{
/// <summary>
/// The author of the beatmaps in this set.
/// </summary>
public User Author;
/// <summary>
/// The date this beatmap set was submitted to the online listing.
/// </summary>
public DateTimeOffset Submitted { get; set; }
/// <summary>
/// The date this beatmap set was ranked.
/// </summary>
public DateTimeOffset? Ranked { get; set; }
/// <summary>
/// The date this beatmap set was last updated.
/// </summary>
public DateTimeOffset? LastUpdated { get; set; }
/// <summary>
/// The different sizes of cover art for this beatmap set.
/// </summary>
@ -22,6 +44,11 @@ namespace osu.Game.Beatmaps
[JsonProperty(@"previewUrl")]
public string Preview { get; set; }
/// <summary>
/// The beats per minute of this beatmap set's song.
/// </summary>
public double BPM { get; set; }
/// <summary>
/// The amount of plays this beatmap set has.
/// </summary>

View File

@ -54,6 +54,8 @@ namespace osu.Game.Configuration
// Graphics
Set(OsuSetting.ShowFpsDisplay, false);
Set(OsuSetting.CursorRotation, true);
Set(OsuSetting.MenuParallax, true);
Set(OsuSetting.SnakingInSliders, true);
@ -96,6 +98,7 @@ namespace osu.Game.Configuration
AudioOffset,
MenuMusic,
MenuVoice,
CursorRotation,
MenuParallax,
BeatmapDetailTab,
Username,

View File

@ -20,13 +20,14 @@ namespace osu.Game.Graphics.Cursor
{
protected override Drawable CreateCursor() => new Cursor();
private Bindable<bool> cursorRotate;
private bool dragging;
private bool startRotation;
protected override bool OnMouseMove(InputState state)
{
if (dragging)
if (cursorRotate && dragging)
{
Debug.Assert(state.Mouse.PositionMouseDown != null);
@ -102,6 +103,12 @@ namespace osu.Game.Graphics.Cursor
ActiveCursor.ScaleTo(0, 500, Easing.In);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
cursorRotate = config.GetBindable<bool>(OsuSetting.CursorRotation);
}
public class Cursor : Container
{
private Container cursorContainer;

View File

@ -49,7 +49,7 @@ namespace osu.Game.IO.Legacy
int len = ReadInt32();
if (len > 0) return ReadBytes(len);
if (len < 0) return null;
return new byte[0];
return Array.Empty<byte>();
}
/// <summary> Reads a char array from the buffer, handling nulls and the array length. </summary>
@ -58,7 +58,7 @@ namespace osu.Game.IO.Legacy
int len = ReadInt32();
if (len > 0) return ReadChars(len);
if (len < 0) return null;
return new char[0];
return Array.Empty<char>();
}
/// <summary> Reads a DateTime from the buffer. </summary>

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Extensions;
using osu.Framework.IO.Network;
namespace osu.Game.Online.API
@ -70,13 +69,11 @@ namespace osu.Game.Online.API
protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}";
private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0)));
private double remainingTime => Math.Max(0, Timeout - (DateTimeOffset.UtcNow - (startTime ?? DateTimeOffset.MinValue)).TotalMilliseconds);
public bool ExceededTimeout => remainingTime == 0;
private double? startTime;
public double StartTime => startTime ?? -1;
private DateTimeOffset? startTime;
protected APIAccess API;
protected WebRequest WebRequest;
@ -96,7 +93,7 @@ namespace osu.Game.Online.API
return;
if (startTime == null)
startTime = DateTime.Now.TotalMilliseconds();
startTime = DateTimeOffset.UtcNow;
if (remainingTime <= 0)
throw new TimeoutException(@"API request timeout hit");

View File

@ -4,7 +4,6 @@
using System;
using System.Globalization;
using Newtonsoft.Json;
using osu.Framework.Extensions;
namespace osu.Game.Online.API
{
@ -22,12 +21,12 @@ namespace osu.Game.Online.API
{
get
{
return AccessTokenExpiry - DateTime.Now.ToUnixTimestamp();
return AccessTokenExpiry - DateTimeOffset.UtcNow.ToUnixTimeSeconds();
}
set
{
AccessTokenExpiry = DateTime.Now.AddSeconds(value).ToUnixTimestamp();
AccessTokenExpiry = DateTimeOffset.Now.AddSeconds(value).ToUnixTimeSeconds();
}
}

View File

@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests
{
private readonly BeatmapInfo beatmap;
private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={beatmap.Path}";
private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={System.Uri.EscapeUriString(beatmap.Path)}";
public GetBeatmapDetailsRequest(BeatmapInfo beatmap)
{

View File

@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets;
using osu.Game.Users;
namespace osu.Game.Online.API.Requests
{
@ -49,6 +50,12 @@ namespace osu.Game.Online.API.Requests
[JsonProperty(@"id")]
private int onlineId { get; set; }
[JsonProperty(@"creator")]
private string creatorUsername;
[JsonProperty(@"user_id")]
private long creatorId = 1;
[JsonProperty(@"beatmaps")]
private IEnumerable<GetBeatmapSetsBeatmapResponse> beatmaps { get; set; }
@ -60,6 +67,11 @@ namespace osu.Game.Online.API.Requests
Metadata = this,
OnlineInfo = new BeatmapSetOnlineInfo
{
Author = new User
{
Id = creatorId,
Username = creatorUsername,
},
Covers = covers,
Preview = preview,
PlayCount = playCount,

View File

@ -23,6 +23,7 @@ namespace osu.Game.Online.API.Requests
req.Method = HttpMethod.POST;
req.AddParameter(@"target_type", message.TargetType.GetDescription());
req.AddParameter(@"target_id", message.TargetId.ToString());
req.AddParameter(@"is_action", message.IsAction.ToString().ToLower());
req.AddParameter(@"message", message.Content);
return req;
@ -30,4 +31,4 @@ namespace osu.Game.Online.API.Requests
protected override string Target => @"chat/messages";
}
}
}

View File

@ -1,25 +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 System;
using osu.Game.Users;
namespace osu.Game.Online.Chat
{
public class ErrorMessage : Message
public class ErrorMessage : InfoMessage
{
private static int errorId = -1;
public ErrorMessage(string message) : base(errorId--)
public ErrorMessage(string message) : base(message)
{
Timestamp = DateTimeOffset.Now;
Content = message;
Sender = new User
{
Username = @"system",
Colour = @"ff0000",
};
Sender.Colour = @"ff0000";
}
}
}
}

View File

@ -0,0 +1,25 @@
// 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.Game.Users;
namespace osu.Game.Online.Chat
{
public class InfoMessage : Message
{
private static int infoID = -1;
public InfoMessage(string message) : base(infoID--)
{
Timestamp = DateTimeOffset.Now;
Content = message;
Sender = new User
{
Username = @"system",
Colour = @"0000ff",
};
}
}
}

View File

@ -23,6 +23,9 @@ namespace osu.Game.Online.Chat
[JsonProperty(@"target_id")]
public int TargetId;
[JsonProperty(@"is_action")]
public bool IsAction;
[JsonProperty(@"timestamp")]
public DateTimeOffset Timestamp;

View File

@ -47,6 +47,8 @@ namespace osu.Game
private UserProfileOverlay userProfile;
private BeatmapSetOverlay beatmapSetOverlay;
public virtual Storage GetStorageForStableInstall() => null;
private Intro intro
@ -187,6 +189,7 @@ namespace osu.Game
Depth = -1
}, overlayContent.Add);
LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add);
LoadComponentAsync(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -2 }, mainContent.Add);
LoadComponentAsync(musicController = new MusicController
{
Depth = -3,
@ -223,6 +226,7 @@ namespace osu.Game
dependencies.Cache(chat);
dependencies.Cache(userProfile);
dependencies.Cache(musicController);
dependencies.Cache(beatmapSetOverlay);
dependencies.Cache(notificationOverlay);
dependencies.Cache(dialogOverlay);

View File

@ -82,6 +82,13 @@ namespace osu.Game
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
private SQLiteConnection createConnection()
{
var conn = Host.Storage.GetDatabase(@"client");
conn.BusyTimeout = new TimeSpan(TimeSpan.TicksPerSecond * 10);
return conn;
}
private SQLiteConnection connection;
[BackgroundDependencyLoader]
@ -90,8 +97,7 @@ namespace osu.Game
dependencies.Cache(this);
dependencies.Cache(LocalConfig);
connection = Host.Storage.GetDatabase(@"client");
connection = createConnection();
connection.CreateTable<StoreVersion>();
dependencies.Cache(API = new APIAccess

View File

@ -0,0 +1,107 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class AuthorInfo : Container
{
private const float height = 50;
private readonly UpdateableAvatar avatar;
private readonly FillFlowContainer fields;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
var i = BeatmapSet.OnlineInfo;
avatar.User = i.Author;
fields.Children = new Drawable[]
{
new Field("made by", i.Author.Username, @"Exo2.0-RegularItalic"),
new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")
{
Margin = new MarginPadding { Top = 5 },
},
};
if (i.Ranked.HasValue)
{
fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
}
else if (i.LastUpdated.HasValue)
{
fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
}
}
}
public AuthorInfo()
{
RelativeSizeAxes = Axes.X;
Height = height;
Children = new Drawable[]
{
avatar = new UpdateableAvatar
{
Size = new Vector2(height),
CornerRadius = 3,
Masking = true,
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
},
},
fields = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Left = height + 5 },
},
};
}
private class Field : FillFlowContainer
{
public Field(string first, string second, string secondFont)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Children = new[]
{
new OsuSpriteText
{
Text = $"{first} ",
TextSize = 13,
},
new OsuSpriteText
{
Text = second,
TextSize = 13,
Font = secondFont,
},
};
}
}
}
}

View File

@ -0,0 +1,130 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Overlays.BeatmapSet
{
public class BasicStats : Container
{
private readonly Statistic length, bpm, circleCount, sliderCount;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##");
}
}
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (value == beatmap) return;
beatmap = value;
length.Value = TimeSpan.FromMilliseconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss");
circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString("N0");
sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString("N0");
}
}
public BasicStats()
{
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new[]
{
length = new Statistic(FontAwesome.fa_clock_o, "Length") { Width = 0.25f },
bpm = new Statistic(FontAwesome.fa_circle, "BPM") { Width = 0.25f },
circleCount = new Statistic(FontAwesome.fa_circle_o, "Circle Count") { Width = 0.25f },
sliderCount = new Statistic(FontAwesome.fa_circle, "Slider Count") { Width = 0.25f },
},
};
}
private class Statistic : Container, IHasTooltip
{
private readonly string name;
private readonly OsuSpriteText value;
public string TooltipText => name;
public string Value
{
get { return value.Text; }
set { this.value.Text = value; }
}
public Statistic(FontAwesome icon, string name)
{
this.name = name;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_square,
Size = new Vector2(13),
Rotation = 45,
Colour = OsuColour.FromHex(@"441288"),
},
new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(13),
Colour = OsuColour.FromHex(@"f7dd55"),
Scale = new Vector2(0.8f),
},
value = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
TextSize = 13,
Font = @"Exo2.0-Bold",
Margin = new MarginPadding { Left = 10 },
},
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
value.Colour = colour.Yellow;
}
}
}
}

View File

@ -0,0 +1,312 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
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.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class BeatmapPicker : Container
{
private const float tile_icon_padding = 7;
private const float tile_spacing = 2;
private readonly DifficultiesContainer difficulties;
private readonly OsuSpriteText version, starRating;
private readonly Statistic plays, favourites;
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
Beatmap.Value = BeatmapSet.Beatmaps.First();
plays.Value = BeatmapSet.OnlineInfo.PlayCount;
favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount;
difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.Select(b => new DifficultySelectorButton(b)
{
State = DifficultySelectorState.NotSelected,
OnHovered = beatmap =>
{
showBeatmap(beatmap);
starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##");
starRating.FadeIn(100);
},
OnClicked = beatmap =>
{
Beatmap.Value = beatmap;
},
});
updateDifficultyButtons();
}
}
public BeatmapPicker()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
difficulties = new DifficultiesContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Left = -(tile_icon_padding + tile_spacing / 2) },
OnLostHover = () =>
{
showBeatmap(Beatmap.Value);
starRating.FadeOut(100);
},
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = 10 },
Spacing = new Vector2(5f),
Children = new[]
{
version = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
TextSize = 20,
Font = @"Exo2.0-Bold",
},
starRating = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
TextSize = 13,
Font = @"Exo2.0-Bold",
Text = "Star Difficulty",
Alpha = 0,
Margin = new MarginPadding { Bottom = 1 },
},
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(10f),
Margin = new MarginPadding { Top = 5 },
Children = new[]
{
plays = new Statistic(FontAwesome.fa_play_circle),
favourites = new Statistic(FontAwesome.fa_heart),
},
},
},
},
};
Beatmap.ValueChanged += b =>
{
showBeatmap(b);
updateDifficultyButtons();
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
starRating.Colour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
// done here so everything can bind in intialization and get the first trigger
Beatmap.TriggerChange();
}
private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version;
private void updateDifficultyButtons()
{
difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected);
}
private class DifficultiesContainer : FillFlowContainer<DifficultySelectorButton>
{
public Action OnLostHover;
protected override void OnHoverLost(InputState state)
{
base.OnHoverLost(state);
OnLostHover?.Invoke();
}
}
private class DifficultySelectorButton : OsuClickableContainer, IStateful<DifficultySelectorState>
{
private const float transition_duration = 100;
private const float size = 52;
private readonly Container bg;
private readonly DifficultyIcon icon;
public readonly BeatmapInfo Beatmap;
public Action<BeatmapInfo> OnHovered;
public Action<BeatmapInfo> OnClicked;
public event Action<DifficultySelectorState> StateChanged;
private DifficultySelectorState state;
public DifficultySelectorState State
{
get { return state; }
set
{
if (value == state) return;
state = value;
StateChanged?.Invoke(State);
if (value == DifficultySelectorState.Selected)
fadeIn();
else
fadeOut();
}
}
public DifficultySelectorButton(BeatmapInfo beatmap)
{
Beatmap = beatmap;
Size = new Vector2(size);
Margin = new MarginPadding { Horizontal = tile_spacing / 2 };
Children = new Drawable[]
{
bg = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 4,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
},
icon = new DifficultyIcon(beatmap)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(size - tile_icon_padding * 2),
Margin = new MarginPadding { Bottom = 1 },
},
};
}
protected override bool OnHover(InputState state)
{
fadeIn();
OnHovered?.Invoke(Beatmap);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
if (State == DifficultySelectorState.NotSelected)
fadeOut();
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
OnClicked?.Invoke(Beatmap);
return base.OnClick(state);
}
private void fadeIn()
{
bg.FadeIn(transition_duration);
icon.FadeIn(transition_duration);
}
private void fadeOut()
{
bg.FadeOut();
icon.FadeTo(0.7f, transition_duration);
}
}
private class Statistic : FillFlowContainer
{
private readonly OsuSpriteText text;
private int value;
public int Value
{
get { return value; }
set
{
this.value = value;
text.Text = Value.ToString(@"N0");
}
}
public Statistic(FontAwesome icon)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Spacing = new Vector2(2f);
Children = new Drawable[]
{
new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Icon = icon,
Shadow = true,
Size = new Vector2(13),
},
text = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = @"Exo2.0-SemiBoldItalic",
TextSize = 14,
},
};
}
}
private enum DifficultySelectorState
{
Selected,
NotSelected,
}
}
}

View File

@ -0,0 +1,118 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select.Details;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class Details : FillFlowContainer
{
private readonly PreviewButton preview;
private readonly BasicStats basic;
private readonly AdvancedStats advanced;
private readonly UserRatings ratings;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
basic.BeatmapSet = preview.BeatmapSet = BeatmapSet;
}
}
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (value == beatmap) return;
beatmap = value;
basic.Beatmap = advanced.Beatmap = Beatmap;
ratings.Metrics = Beatmap.Metrics;
}
}
public Details()
{
Width = BeatmapSetOverlay.RIGHT_WIDTH;
AutoSizeAxes = Axes.Y;
Spacing = new Vector2(1f);
Children = new Drawable[]
{
preview = new PreviewButton
{
RelativeSizeAxes = Axes.X,
},
new DetailBox
{
Child = basic = new BasicStats
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Vertical = 10 },
},
},
new DetailBox
{
Child = advanced = new AdvancedStats
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Vertical = 7.5f },
},
},
new DetailBox
{
Child = ratings = new UserRatings
{
RelativeSizeAxes = Axes.X,
Height = 95,
Margin = new MarginPadding { Top = 10 },
},
},
};
}
private class DetailBox : Container
{
private readonly Container content;
protected override Container<Drawable> Content => content;
public DetailBox()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
content = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = 15 },
},
};
}
}
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Overlays.BeatmapSet
{
public class DownloadButton : HeaderButton
{
public DownloadButton(string title, string subtitle)
{
Width = 120;
RelativeSizeAxes = Axes.Y;
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 10 },
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new[]
{
new OsuSpriteText
{
Text = title,
TextSize = 13,
Font = @"Exo2.0-Bold",
},
new OsuSpriteText
{
Text = subtitle,
TextSize = 11,
Font = @"Exo2.0-Bold",
},
},
},
new SpriteIcon
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Icon = FontAwesome.fa_download,
Size = new Vector2(16),
Margin = new MarginPadding { Right = 5 },
},
},
};
}
}
}

View File

@ -0,0 +1,80 @@
// 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.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using OpenTK;
namespace osu.Game.Overlays.BeatmapSet
{
public class FavouriteButton : HeaderButton
{
public readonly Bindable<bool> Favourited = new Bindable<bool>();
public FavouriteButton()
{
RelativeSizeAxes = Axes.Y;
Container pink;
SpriteIcon icon;
Children = new Drawable[]
{
pink = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"9f015f"),
},
new Triangles
{
RelativeSizeAxes = Axes.Both,
ColourLight = OsuColour.FromHex(@"cb2187"),
ColourDark = OsuColour.FromHex(@"9f015f"),
TriangleScale = 1.5f,
},
},
},
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_heart_o,
Size = new Vector2(18),
Shadow = false,
},
};
Favourited.ValueChanged += value =>
{
if (value)
{
pink.FadeIn(200);
icon.Icon = FontAwesome.fa_heart;
}
else
{
pink.FadeOut(200);
icon.Icon = FontAwesome.fa_heart_o;
}
};
Action = () => Favourited.Value = !Favourited.Value;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Width = DrawHeight;
}
}
}

View File

@ -0,0 +1,228 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class Header : Container
{
private const float transition_duration = 250;
private const float tabs_height = 50;
private const float buttons_height = 45;
private const float buttons_spacing = 5;
private readonly Box tabsBg;
private readonly Container coverContainer;
private readonly OsuSpriteText title, artist;
private readonly AuthorInfo author;
private readonly Details details;
private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
Picker.BeatmapSet = author.BeatmapSet = details.BeatmapSet = BeatmapSet;
title.Text = BeatmapSet.Metadata.Title;
artist.Text = BeatmapSet.Metadata.Artist;
cover?.FadeOut(400, Easing.Out);
coverContainer.Add(cover = new DelayedLoadWrapper(new BeatmapSetCover(BeatmapSet)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
OnLoadComplete = d =>
{
d.FadeInFromZero(400, Easing.Out);
},
})
{
RelativeSizeAxes = Axes.Both,
TimeBeforeLoad = 300
});
}
}
public Header()
{
RelativeSizeAxes = Axes.X;
Height = 400;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
};
Container noVideoButtons;
FillFlowContainer videoButtons;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Height = tabs_height,
Children = new[]
{
tabsBg = new Box
{
RelativeSizeAxes = Axes.Both,
},
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = tabs_height },
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
coverContainer = new Container
{
RelativeSizeAxes = Axes.Both,
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.3f), Color4.Black.Opacity(0.8f)),
},
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 20, Bottom = 30, Horizontal = BeatmapSetOverlay.X_PADDING },
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Height = 113,
Child = Picker = new BeatmapPicker(),
},
title = new OsuSpriteText
{
Font = @"Exo2.0-BoldItalic",
TextSize = 37,
},
artist = new OsuSpriteText
{
Font = @"Exo2.0-SemiBoldItalic",
TextSize = 25,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 20 },
Child = author = new AuthorInfo(),
},
new Container
{
RelativeSizeAxes = Axes.X,
Height = buttons_height,
Margin = new MarginPadding { Top = 10 },
Children = new Drawable[]
{
new FavouriteButton(),
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
Children = new Drawable[]
{
noVideoButtons = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
Child = new DownloadButton("Download", @""),
},
videoButtons = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Spacing = new Vector2(buttons_spacing),
Alpha = 0f,
Children = new[]
{
new DownloadButton("Download", "with Video"),
new DownloadButton("Download", "without Video"),
},
},
},
},
},
},
},
},
},
details = new Details
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding { Right = BeatmapSetOverlay.X_PADDING },
},
},
},
};
Picker.Beatmap.ValueChanged += b =>
{
details.Beatmap = b;
if (b.OnlineInfo.HasVideo)
{
noVideoButtons.FadeOut(transition_duration);
videoButtons.FadeIn(transition_duration);
}
else
{
noVideoButtons.FadeIn(transition_duration);
videoButtons.FadeOut(transition_duration);
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabsBg.Colour = colours.Gray3;
}
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.BeatmapSet
{
public class HeaderButton : OsuClickableContainer
{
private readonly Container content;
protected override Container<Drawable> Content => content;
public HeaderButton()
{
CornerRadius = 3;
Masking = true;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"094c5f"),
},
new Triangles
{
RelativeSizeAxes = Axes.Both,
ColourLight = OsuColour.FromHex(@"0f7c9b"),
ColourDark = OsuColour.FromHex(@"094c5f"),
TriangleScale = 1.5f,
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
},
};
}
}
}

View File

@ -0,0 +1,196 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class Info : Container
{
private const float transition_duration = 250;
private const float metadata_width = 225;
private const float spacing = 20;
private readonly MetadataSection description, source, tags;
private readonly Box successRateBackground;
private readonly SuccessRate successRate;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
source.Text = BeatmapSet.Metadata.Source;
tags.Text = BeatmapSet.Metadata.Tags;
}
}
public BeatmapInfo Beatmap
{
get { return successRate.Beatmap; }
set { successRate.Beatmap = value; }
}
public Info()
{
RelativeSizeAxes = Axes.X;
Height = 220;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 15, Horizontal = BeatmapSetOverlay.X_PADDING },
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = metadata_width + BeatmapSetOverlay.RIGHT_WIDTH + spacing * 2 },
Child = new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = description = new MetadataSection("Description"),
},
},
new ScrollContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = metadata_width,
ScrollbarVisible = false,
Padding = new MarginPadding { Horizontal = 10 },
Margin = new MarginPadding { Right = BeatmapSetOverlay.RIGHT_WIDTH + spacing },
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = transition_duration,
Children = new[]
{
source = new MetadataSection("Source"),
tags = new MetadataSection("Tags"),
},
},
},
new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = BeatmapSetOverlay.RIGHT_WIDTH,
Children = new Drawable[]
{
successRateBackground = new Box
{
RelativeSizeAxes = Axes.Both,
},
successRate = new SuccessRate
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 20, Horizontal = 15 },
},
},
},
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
successRateBackground.Colour = colours.GrayE;
source.TextColour = description.TextColour = colours.Gray5;
tags.TextColour = colours.BlueDark;
}
private class MetadataSection : FillFlowContainer
{
private readonly OsuSpriteText header;
private readonly TextFlowContainer textFlow;
public string Text
{
set
{
if (string.IsNullOrEmpty(value))
{
this.FadeOut(transition_duration);
return;
}
this.FadeIn(transition_duration);
textFlow.Clear();
textFlow.AddText(value, s => s.TextSize = 14);
}
}
public Color4 TextColour
{
get { return textFlow.Colour; }
set { textFlow.Colour = value; }
}
public MetadataSection(string title)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Spacing = new Vector2(5f);
InternalChildren = new Drawable[]
{
header = new OsuSpriteText
{
Text = title,
Font = @"Exo2.0-Bold",
TextSize = 14,
Shadow = false,
Margin = new MarginPadding { Top = 20 },
},
textFlow = new TextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
header.Colour = colours.Gray5;
}
}
}
}

View File

@ -0,0 +1,218 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
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.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class PreviewButton : OsuClickableContainer
{
private const float transition_duration = 500;
private readonly Container audioWrapper;
private readonly Box bg, progress;
private readonly SpriteIcon icon;
private readonly LoadingAnimation loadingAnimation;
private Track preview;
private bool loading
{
set
{
if (value)
{
loadingAnimation.Show();
icon.FadeOut(transition_duration * 5, Easing.OutQuint);
}
else
{
loadingAnimation.Hide();
icon.FadeIn(transition_duration, Easing.OutQuint);
}
}
}
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
Playing = false;
preview = null;
}
}
private bool playing;
public bool Playing
{
get { return playing; }
set
{
if (value == playing) return;
playing = value;
if (preview == null)
{
loading = true;
audioWrapper.Child = new AsyncLoadWrapper(new AudioLoadWrapper(BeatmapSet)
{
OnLoadComplete = d =>
{
loading = false;
preview = (d as AudioLoadWrapper)?.Preview;
Playing = Playing;
updatePlayingState();
},
});
return;
}
updatePlayingState();
}
}
public PreviewButton()
{
Height = 42;
Children = new Drawable[]
{
audioWrapper = new Container(),
bg = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.25f),
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 3,
Child = progress = new Box
{
RelativeSizeAxes = Axes.Both,
Width = 0f,
Alpha = 0f,
},
},
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_play,
Size = new Vector2(18),
Shadow = false,
},
loadingAnimation = new LoadingAnimation
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
};
Action = () => Playing = !Playing;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
progress.Colour = colours.Yellow;
}
protected override void Update()
{
base.Update();
if (Playing && preview != null)
{
progress.Width = (float)(preview.CurrentTime / preview.Length);
if (preview.HasCompleted)
{
Playing = false;
preview = null;
}
}
}
protected override void Dispose(bool isDisposing)
{
Playing = false;
base.Dispose(isDisposing);
}
protected override bool OnHover(InputState state)
{
bg.FadeColour(Color4.Black.Opacity(0.5f), 100);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
bg.FadeColour(Color4.Black.Opacity(0.25f), 100);
base.OnHoverLost(state);
}
private void updatePlayingState()
{
if (preview == null) return;
if (Playing)
{
icon.Icon = FontAwesome.fa_stop;
progress.FadeIn(100);
preview.Seek(0);
preview.Start();
}
else
{
icon.Icon = FontAwesome.fa_play;
progress.FadeOut(100);
preview.Stop();
}
}
private class AudioLoadWrapper : Drawable
{
private readonly string preview;
public Track Preview;
public AudioLoadWrapper(BeatmapSetInfo set)
{
preview = set.OnlineInfo.Preview;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
if (!string.IsNullOrEmpty(preview))
{
Preview = audio.Track.Get(preview);
Preview.Volume.Value = 0.5;
}
}
}
}
}

View File

@ -0,0 +1,113 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select.Details;
namespace osu.Game.Overlays.BeatmapSet
{
public class SuccessRate : Container
{
private readonly FillFlowContainer header;
private readonly OsuSpriteText successRateLabel, successPercent, graphLabel;
private readonly Bar successRate;
private readonly Container percentContainer;
private readonly FailRetryGraph graph;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (value == beatmap) return;
beatmap = value;
var rate = (float)beatmap.OnlineInfo.PassCount / beatmap.OnlineInfo.PlayCount;
successPercent.Text = $"{Math.Round(rate * 100)}%";
successRate.Length = rate;
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
graph.Metrics = Beatmap.Metrics;
}
}
public SuccessRate()
{
Children = new Drawable[]
{
header = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
successRateLabel = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Success Rate",
TextSize = 13,
},
successRate = new Bar
{
RelativeSizeAxes = Axes.X,
Height = 5,
Margin = new MarginPadding { Top = 5 },
},
percentContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0f,
Child = successPercent = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopCentre,
Text = @"0%",
TextSize = 13,
},
},
graphLabel = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Points of Failure",
TextSize = 13,
Margin = new MarginPadding { Vertical = 20 },
},
},
},
graph = new FailRetryGraph
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5;
successRate.AccentColour = colours.Green;
successRate.BackgroundColour = colours.GrayD;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
graph.Padding = new MarginPadding { Top = header.DrawHeight };
}
}
}

View File

@ -0,0 +1,99 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.BeatmapSet;
namespace osu.Game.Overlays
{
public class BeatmapSetOverlay : WaveOverlayContainer
{
public const float X_PADDING = 40;
public const float RIGHT_WIDTH = 275;
private readonly Header header;
private readonly Info info;
public BeatmapSetOverlay()
{
FirstWaveColour = OsuColour.Gray(0.4f);
SecondWaveColour = OsuColour.Gray(0.3f);
ThirdWaveColour = OsuColour.Gray(0.2f);
FourthWaveColour = OsuColour.Gray(0.1f);
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
RelativeSizeAxes = Axes.Both;
Width = 0.85f;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
},
new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = new ReverseChildIDFillFlowContainer<Drawable>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
header = new Header(),
info = new Info(),
},
},
},
};
header.Picker.Beatmap.ValueChanged += b => info.Beatmap = b;
}
protected override void PopIn()
{
base.PopIn();
FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In);
}
protected override void PopOut()
{
base.PopOut();
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
}
protected override bool OnClick(InputState state)
{
State = Visibility.Hidden;
return true;
}
public void ShowBeatmapSet(BeatmapSetInfo set)
{
header.BeatmapSet = info.BeatmapSet = set;
Show();
}
}
}

View File

@ -63,6 +63,7 @@ namespace osu.Game.Overlays.Chat
private const float padding = 15;
private const float message_padding = 200;
private const float action_padding = 3;
private const float text_size = 20;
private Color4 customUsernameColour;
@ -194,6 +195,8 @@ namespace osu.Game.Overlays.Chat
}
}
};
if (message.IsAction && senderHasBackground)
contentFlow.Colour = OsuColour.FromHex(message.Sender.Colour);
updateMessageContent();
FinishTransforms(true);
@ -206,7 +209,17 @@ namespace osu.Game.Overlays.Chat
timestamp.Text = $@"{message.Timestamp.LocalDateTime:HH:mm:ss}";
username.Text = $@"{message.Sender.Username}" + (senderHasBackground ? "" : ":");
contentFlow.Text = message.Content;
if (message.IsAction)
{
contentFlow.Clear();
contentFlow.AddText("[", sprite => sprite.Padding = new MarginPadding { Right = action_padding });
contentFlow.AddText(message.Content, sprite => sprite.Font = @"Exo2.0-MediumItalic");
contentFlow.AddText("]", sprite => sprite.Padding = new MarginPadding { Left = action_padding });
}
else
contentFlow.Text = message.Content;
}
private class MessageSender : ClickableContainer, IHasContextMenu

View File

@ -465,7 +465,7 @@ namespace osu.Game.Overlays
textbox.Text = string.Empty;
if (string.IsNullOrEmpty(postText))
if (string.IsNullOrWhiteSpace(postText))
return;
var target = currentChannel;
@ -478,11 +478,36 @@ namespace osu.Game.Overlays
return;
}
bool isAction = false;
if (postText[0] == '/')
{
// TODO: handle commands
target.AddNewMessages(new ErrorMessage("Chat commands are not supported yet!"));
return;
string[] parameters = postText.Substring(1).Split(new[] { ' ' }, 2);
string command = parameters[0];
string content = parameters.Length == 2 ? parameters[1] : string.Empty;
switch (command)
{
case "me":
if (string.IsNullOrWhiteSpace(content))
{
currentChannel.AddNewMessages(new ErrorMessage("Usage: /me [action]"));
return;
}
isAction = true;
postText = content;
break;
case "help":
currentChannel.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]"));
return;
default:
currentChannel.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help"));
return;
}
}
var message = new LocalEchoMessage
@ -491,6 +516,7 @@ namespace osu.Game.Overlays
Timestamp = DateTimeOffset.Now,
TargetType = TargetType.Channel, //TODO: read this from channel
TargetId = target.Id,
IsAction = isAction,
Content = postText
};

View File

@ -12,7 +12,6 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Framework.Input;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.Direct
@ -164,6 +163,15 @@ namespace osu.Game.Overlays.Direct
},
},
},
new DownloadButton
{
Size = new Vector2(30),
Margin = new MarginPadding(horizontal_padding),
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Colour = colours.Gray5,
Action = StartDownload
},
},
},
},
@ -204,11 +212,5 @@ namespace osu.Game.Overlays.Direct
if (PreviewPlaying && playButton.Track != null)
progressBar.Width = (float)(playButton.Track.CurrentTime / playButton.Track.Length);
}
protected override bool OnClick(InputState state)
{
StartDownload();
return true;
}
}
}

View File

@ -11,10 +11,8 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Localisation;
using osu.Framework.Input;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.Direct
@ -179,47 +177,5 @@ namespace osu.Game.Overlays.Direct
if (PreviewPlaying && playButton.Track != null)
progressBar.Width = (float)(playButton.Track.CurrentTime / playButton.Track.Length);
}
private class DownloadButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
public DownloadButton()
{
Children = new Drawable[]
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(30),
Icon = FontAwesome.fa_osu_chevron_down_o,
},
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.9f, 1000, Easing.Out);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnHover(InputState state)
{
icon.ScaleTo(1.1f, 500, Easing.OutElastic);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
}
}
}
}

View File

@ -38,6 +38,7 @@ namespace osu.Game.Overlays.Direct
private ProgressBar progressBar;
private BeatmapManager beatmaps;
private NotificationOverlay notifications;
private BeatmapSetOverlay beatmapSetOverlay;
public readonly Bindable<bool> PreviewPlaying = new Bindable<bool>();
protected abstract PlayButton PlayButton { get; }
@ -67,11 +68,12 @@ namespace osu.Game.Overlays.Direct
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications)
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications, BeatmapSetOverlay beatmapSetOverlay)
{
this.api = api;
this.beatmaps = beatmaps;
this.notifications = notifications;
this.beatmapSetOverlay = beatmapSetOverlay;
AddInternal(content = new Container
{
@ -125,6 +127,14 @@ namespace osu.Game.Overlays.Direct
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
ShowInformation();
return true;
}
protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo);
protected void StartDownload()
{
if (!api.LocalUser.Value.IsSupporter)

View File

@ -0,0 +1,53 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Direct
{
public class DownloadButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
public DownloadButton()
{
Children = new Drawable[]
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(30),
Icon = FontAwesome.fa_osu_chevron_down_o,
},
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.9f, 1000, Easing.Out);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnHover(InputState state)
{
icon.ScaleTo(1.1f, 500, Easing.OutElastic);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
}
}
}

View File

@ -127,7 +127,7 @@ namespace osu.Game.Overlays.Mods
if (mod == null)
{
Mods = new Mod[0];
Mods = Array.Empty<Mod>();
Alpha = 0;
}
else

View File

@ -1,10 +1,25 @@
// 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.Game.Configuration;
namespace osu.Game.Overlays.Settings.Sections.Graphics
{
public class DetailSettings : SettingsSubsection
{
protected override string Header => "Detail Settings";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new[]
{
new SettingsCheckbox
{
LabelText = "Rotate cursor when dragging",
Bindable = config.GetBindable<bool>(OsuSetting.CursorRotation)
},
};
}
}
}

View File

@ -121,13 +121,14 @@ namespace osu.Game.Screens.Menu
};
}
private bool rightward;
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
if (!IsHovered) return;
bool rightward = beatIndex % 2 == 1;
double duration = timingPoint.BeatLength / 2;
icon.RotateTo(rightward ? 10 : -10, duration * 2, Easing.InOutSine);
@ -139,6 +140,8 @@ namespace osu.Game.Screens.Menu
i => i.MoveToY(0, duration, Easing.In),
i => i.ScaleTo(new Vector2(1, 0.9f), duration, Easing.In)
);
rightward = !rightward;
}
protected override bool OnHover(InputState state)
@ -152,7 +155,7 @@ namespace osu.Game.Screens.Menu
double duration = TimeUntilNextBeat;
icon.ClearTransforms();
icon.RotateTo(10, duration, Easing.InOutSine);
icon.RotateTo(rightward ? -10 : 10, duration, Easing.InOutSine);
icon.ScaleTo(new Vector2(1, 0.9f), duration, Easing.Out);
return true;
}

View File

@ -40,9 +40,9 @@ namespace osu.Game.Screens.Select.Details
firstValue.Value = Beatmap?.Difficulty?.CircleSize ?? 0;
}
hpDrain.Value = beatmap.Difficulty.DrainRate;
accuracy.Value = beatmap.Difficulty.OverallDifficulty;
approachRate.Value = beatmap.Difficulty.ApproachRate;
hpDrain.Value = beatmap.Difficulty?.DrainRate ?? 0;
accuracy.Value = beatmap.Difficulty?.OverallDifficulty ?? 0;
approachRate.Value = beatmap.Difficulty?.ApproachRate ?? 0;
starDifficulty.Value = (float)beatmap.StarDifficulty;
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
@ -58,6 +59,31 @@ namespace osu.Game.Screens.Select
Action = action,
});
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
/// <param name="text">Text on the button.</param>
/// <param name="colour">Colour of the button.</param>
/// <param name="hotkey">Hotkey of the button.</param>
/// <param name="overlay">The <see cref="OverlayContainer"/> to be toggled by this button.</param>
/// <param name="depth">
/// <para>Higher depth to be put on the left, and lower to be put on the right.</para>
/// <para>Notice this is different to <see cref="Options.BeatmapOptionsOverlay"/>!</para>
/// </param>
public void AddButton(string text, Color4 colour, OverlayContainer overlay, Key? hotkey = null, float depth = 0)
{
overlays.Add(overlay);
AddButton(text, colour, () =>
{
foreach (var o in overlays)
{
if (o == overlay)
o.ToggleVisibility();
else
o.Hide();
}
}, hotkey, depth);
}
private void updateModeLight() => modeLight.FadeColour(buttons.FirstOrDefault(b => b.IsHovered)?.SelectedColour ?? Color4.Transparent, TRANSITION_LENGTH, Easing.OutQuint);
public Footer()

View File

@ -45,7 +45,7 @@ namespace osu.Game.Screens.Select
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Footer.AddButton(@"mods", colours.Yellow, modSelect.ToggleVisibility, Key.F1, float.MaxValue);
Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue);
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2);

View File

@ -164,7 +164,7 @@ namespace osu.Game.Screens.Select
if (Footer != null)
{
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
Footer.AddButton(@"options", colours.Blue, BeatmapOptions.ToggleVisibility, Key.F3);
Footer.AddButton(@"options", colours.Blue, BeatmapOptions, Key.F3);
BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, () => promptDelete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
}

View File

@ -0,0 +1,385 @@
// 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 osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
internal class TestCaseBeatmapSetOverlay : OsuTestCase
{
public override string Description => @"view online beatmap sets";
private readonly BeatmapSetOverlay overlay;
public TestCaseBeatmapSetOverlay()
{
Add(overlay = new BeatmapSetOverlay());
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
var mania = rulesets.GetRuleset(3);
var taiko = rulesets.GetRuleset(1);
AddStep(@"show first", () =>
{
overlay.ShowBeatmapSet(new BeatmapSetInfo
{
Metadata = new BeatmapMetadata
{
Title = @"Lachryma <Re:QueenM>",
Artist = @"Kaneko Chiharu",
Source = @"SOUND VOLTEX III GRAVITY WARS",
Tags = @"sdvx grace the 5th kac original song contest konami bemani",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
Preview = @"https://b.ppy.sh/preview/415886.mp3",
PlayCount = 681380,
FavouriteCount = 356,
Submitted = new DateTime(2016, 2, 10),
Ranked = new DateTime(2016, 6, 19),
BPM = 236,
Author = new User
{
Username = @"Fresh Chicken",
Id = 3984370,
},
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778",
},
},
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
StarDifficulty = 1.36,
Version = @"BASIC",
Ruleset = mania,
Difficulty = new BeatmapDifficulty
{
CircleSize = 4,
DrainRate = 6.5f,
OverallDifficulty = 6.5f,
ApproachRate = 5,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 115000,
HasVideo = false,
CircleCount = 265,
SliderCount = 71,
PlayCount = 47906,
PassCount = 19899,
},
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),
},
},
new BeatmapInfo
{
StarDifficulty = 2.22,
Version = @"NOVICE",
Ruleset = mania,
Difficulty = new BeatmapDifficulty
{
CircleSize = 4,
DrainRate = 7,
OverallDifficulty = 7,
ApproachRate = 5,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 118000,
HasVideo = true,
CircleCount = 592,
SliderCount = 62,
PlayCount = 162021,
PassCount = 72116,
},
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),
},
},
new BeatmapInfo
{
StarDifficulty = 3.49,
Version = @"ADVANCED",
Ruleset = mania,
Difficulty = new BeatmapDifficulty
{
CircleSize = 4,
DrainRate = 7.5f,
OverallDifficulty = 7.5f,
ApproachRate = 5,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 118000,
HasVideo = false,
CircleCount = 1042,
SliderCount = 79,
PlayCount = 225178,
PassCount = 73001,
},
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),
},
},
new BeatmapInfo
{
StarDifficulty = 4.24,
Version = @"EXHAUST",
Ruleset = mania,
Difficulty = new BeatmapDifficulty
{
CircleSize = 4,
DrainRate = 8,
OverallDifficulty = 8,
ApproachRate = 5,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 118000,
HasVideo = false,
CircleCount = 1352,
SliderCount = 69,
PlayCount = 131545,
PassCount = 42703,
},
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),
},
},
new BeatmapInfo
{
StarDifficulty = 5.26,
Version = @"GRAVITY",
Ruleset = mania,
Difficulty = new BeatmapDifficulty
{
CircleSize = 4,
DrainRate = 8.5f,
OverallDifficulty = 8.5f,
ApproachRate = 5,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 118000,
HasVideo = false,
CircleCount = 1730,
SliderCount = 115,
PlayCount = 117673,
PassCount = 24241,
},
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),
},
},
},
});
});
AddStep(@"show second", () =>
{
overlay.ShowBeatmapSet(new BeatmapSetInfo
{
Metadata = new BeatmapMetadata
{
Title = @"Soumatou Labyrinth",
Artist = @"Yunomi with Momobako&miko",
Tags = @"mmbk.com yuzu__rinrin charlotte",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
Preview = @"https://b.ppy.sh/preview/625493.mp3",
PlayCount = 22996,
FavouriteCount = 58,
Submitted = new DateTime(2016, 6, 11),
Ranked = new DateTime(2016, 7, 12),
BPM = 160,
Author = new User
{
Username = @"komasy",
Id = 1980256,
},
Covers = new BeatmapSetOnlineCovers
{
Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472",
},
},
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
StarDifficulty = 1.40,
Version = @"yzrin's Kantan",
Ruleset = taiko,
Difficulty = new BeatmapDifficulty
{
CircleSize = 2,
DrainRate = 7,
OverallDifficulty = 3,
ApproachRate = 10,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 193000,
HasVideo = false,
CircleCount = 262,
SliderCount = 0,
PlayCount = 3952,
PassCount = 1373,
},
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),
},
},
new BeatmapInfo
{
StarDifficulty = 2.23,
Version = @"Futsuu",
Ruleset = taiko,
Difficulty = new BeatmapDifficulty
{
CircleSize = 2,
DrainRate = 6,
OverallDifficulty = 4,
ApproachRate = 10,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 193000,
HasVideo = false,
CircleCount = 464,
SliderCount = 0,
PlayCount = 4833,
PassCount = 920,
},
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),
},
},
new BeatmapInfo
{
StarDifficulty = 3.19,
Version = @"Muzukashii",
Ruleset = taiko,
Difficulty = new BeatmapDifficulty
{
CircleSize = 2,
DrainRate = 6,
OverallDifficulty = 5,
ApproachRate = 10,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 193000,
HasVideo = false,
CircleCount = 712,
SliderCount = 0,
PlayCount = 4405,
PassCount = 854,
},
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),
},
},
new BeatmapInfo
{
StarDifficulty = 3.97,
Version = @"Charlotte's Oni",
Ruleset = taiko,
Difficulty = new BeatmapDifficulty
{
CircleSize = 5,
DrainRate = 6,
OverallDifficulty = 5.5f,
ApproachRate = 10,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 193000,
HasVideo = false,
CircleCount = 943,
SliderCount = 0,
PlayCount = 3950,
PassCount = 693,
},
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),
},
},
new BeatmapInfo
{
StarDifficulty = 5.08,
Version = @"Labyrinth Oni",
Ruleset = taiko,
Difficulty = new BeatmapDifficulty
{
CircleSize = 5,
DrainRate = 5,
OverallDifficulty = 6,
ApproachRate = 10,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 193000,
HasVideo = false,
CircleCount = 1068,
SliderCount = 0,
PlayCount = 5856,
PassCount = 1207,
},
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),
},
},
},
});
});
AddStep(@"hide", overlay.Hide);
AddStep(@"show without reload", overlay.Show);
}
}
}

View File

@ -372,6 +372,7 @@
<Compile Include="Online\API\Requests\PostMessageRequest.cs" />
<Compile Include="Online\Chat\Channel.cs" />
<Compile Include="Online\Chat\ErrorMessage.cs" />
<Compile Include="Online\Chat\InfoMessage.cs" />
<Compile Include="Online\Chat\LocalEchoMessage.cs" />
<Compile Include="Online\Chat\Message.cs" />
<Compile Include="Online\Multiplayer\GameType.cs" />
@ -398,6 +399,7 @@
<Compile Include="Overlays\Direct\DirectGridPanel.cs" />
<Compile Include="Overlays\Direct\DirectListPanel.cs" />
<Compile Include="Overlays\Direct\DirectPanel.cs" />
<Compile Include="Overlays\Direct\DownloadButton.cs" />
<Compile Include="Overlays\Direct\FilterControl.cs" />
<Compile Include="Overlays\Direct\Header.cs" />
<Compile Include="Overlays\KeyBindingOverlay.cs" />
@ -774,6 +776,19 @@
<Compile Include="Users\UserPanel.cs" />
<Compile Include="Users\UserStatistics.cs" />
<Compile Include="Users\UserStatus.cs" />
<Compile Include="Overlays\BeatmapSetOverlay.cs" />
<Compile Include="Overlays\BeatmapSet\Info.cs" />
<Compile Include="Overlays\BeatmapSet\Header.cs" />
<Compile Include="Overlays\BeatmapSet\AuthorInfo.cs" />
<Compile Include="Overlays\BeatmapSet\BeatmapPicker.cs" />
<Compile Include="Overlays\BeatmapSet\HeaderButton.cs" />
<Compile Include="Overlays\BeatmapSet\Details.cs" />
<Compile Include="Overlays\BeatmapSet\FavouriteButton.cs" />
<Compile Include="Overlays\BeatmapSet\DownloadButton.cs" />
<Compile Include="Overlays\BeatmapSet\BasicStats.cs" />
<Compile Include="Overlays\BeatmapSet\SuccessRate.cs" />
<Compile Include="Overlays\BeatmapSet\PreviewButton.cs" />
<Compile Include="Tests\Visual\TestCaseBeatmapSetOverlay.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="lazer.ico" />

View File

@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Mania", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Desktop.Deploy", "osu.Desktop.Deploy\osu.Desktop.Deploy.csproj", "{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tests", "osu.Game.Tests\osu.Game.Tests.csproj", "{54377672-20B1-40AF-8087-5CF73BF3953A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -71,6 +73,12 @@ Global
{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU
{54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54377672-20B1-40AF-8087-5CF73BF3953A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54377672-20B1-40AF-8087-5CF73BF3953A}.Release|Any CPU.Build.0 = Release|Any CPU
{54377672-20B1-40AF-8087-5CF73BF3953A}.VisualTests|Any CPU.ActiveCfg = Release|Any CPU
{54377672-20B1-40AF-8087-5CF73BF3953A}.VisualTests|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE