1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-28 02:02:53 +08:00

Merge branch 'master' of https://github.com/ppy/osu into intro-improvements

This commit is contained in:
ColdVolcano 2017-04-24 22:42:28 -05:00
commit 630c556b80
43 changed files with 651 additions and 337 deletions

16
.vscode/tasks.json vendored
View File

@ -10,10 +10,16 @@
"showOutput": "silent",
"command": "msbuild",
"args": [
// Ask msbuild to generate full paths for file names.
"/property:GenerateFullPaths=true",
"/property:DebugType=portable"
],
"windows": {
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/m" //parallel compiling support. doesn't work well with mono atm
]
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile",
"isBuildCommand": true
@ -29,6 +35,14 @@
"/property:DebugType=portable",
"/target:Clean,Build"
],
"windows": {
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/target:Clean,Build",
"/m" //parallel compiling support. doesn't work well with mono atm
]
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile",
"isBuildCommand": true

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
@ -62,15 +61,12 @@ namespace osu.Desktop.VisualTests.Tests
add(new DrawableSlider(new Slider
{
StartTime = framedClock.CurrentTime + 600,
CurveObject = new CurvedHitObject
ControlPoints = new List<Vector2>
{
ControlPoints = new List<Vector2>
{
new Vector2(-200, 0),
new Vector2(400, 0),
},
Distance = 400
new Vector2(-200, 0),
new Vector2(400, 0),
},
Distance = 400,
Position = new Vector2(-200, 0),
Velocity = 1,
TickDistance = 100,

View File

@ -19,10 +19,10 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
{
IHasCurve curveData = original as IHasCurve;
IHasEndTime endTimeData = original as IHasEndTime;
IHasPosition positionData = original as IHasPosition;
IHasCombo comboData = original as IHasCombo;
var curveData = original as IHasCurve;
var endTimeData = original as IHasEndTime;
var positionData = original as IHasPosition;
var comboData = original as IHasCombo;
if (curveData != null)
{
@ -30,7 +30,11 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
{
StartTime = original.StartTime,
Samples = original.Samples,
CurveObject = curveData,
ControlPoints = curveData.ControlPoints,
CurveType = curveData.CurveType,
Distance = curveData.Distance,
RepeatSamples = curveData.RepeatSamples,
RepeatCount = curveData.RepeatCount,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false
};

View File

@ -20,24 +20,33 @@ namespace osu.Game.Rulesets.Osu.Objects
/// </summary>
private const float base_scoring_distance = 100;
public IHasCurve CurveObject { get; set; }
public SliderCurve Curve => CurveObject.Curve;
public readonly SliderCurve Curve = new SliderCurve();
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
public double Duration => EndTime - StartTime;
public override Vector2 EndPosition => PositionAt(1);
public Vector2 PositionAt(double progress) => CurveObject.PositionAt(progress);
public double ProgressAt(double progress) => CurveObject.ProgressAt(progress);
public int RepeatAt(double progress) => CurveObject.RepeatAt(progress);
public List<Vector2> ControlPoints
{
get { return Curve.ControlPoints; }
set { Curve.ControlPoints = value; }
}
public List<Vector2> ControlPoints => CurveObject.ControlPoints;
public CurveType CurveType => CurveObject.CurveType;
public double Distance => CurveObject.Distance;
public CurveType CurveType
{
get { return Curve.CurveType; }
set { Curve.CurveType = value; }
}
public int RepeatCount => CurveObject.RepeatCount;
public double Distance
{
get { return Curve.Distance; }
set { Curve.Distance = value; }
}
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
public int RepeatCount { get; set; } = 1;
private int stackHeight;
public override int StackHeight
@ -63,6 +72,18 @@ namespace osu.Game.Rulesets.Osu.Objects
TickDistance = scoringDistance / difficulty.SliderTickRate;
}
public Vector2 PositionAt(double progress) => Curve.PositionAt(ProgressAt(progress));
public double ProgressAt(double progress)
{
double p = progress * RepeatCount % 1;
if (RepeatAt(progress) % 2 == 1)
p = 1 - p;
return p;
}
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
public IEnumerable<SliderTick> Ticks
{
get

View File

@ -3,7 +3,6 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using System;
@ -28,7 +27,7 @@ namespace osu.Game.Rulesets.Osu
protected override void PreprocessHitObjects()
{
foreach (var h in Objects)
(h as IHasCurve)?.Curve?.Calculate();
(h as Slider)?.Curve?.Calculate();
}
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
@ -190,4 +189,4 @@ namespace osu.Game.Rulesets.Osu
Aim,
};
}
}
}

View File

@ -0,0 +1,69 @@
// 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.Containers;
using osu.Game.Database;
using osu.Game.Graphics;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
internal class DifficultyColouredContainer : Container, IHasAccentColour
{
public Color4 AccentColour { get; set; }
private readonly BeatmapInfo beatmap;
private OsuColour palette;
public DifficultyColouredContainer(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
}
[BackgroundDependencyLoader]
private void load(OsuColour palette)
{
this.palette = palette;
AccentColour = getColour(beatmap);
}
private enum DifficultyRating
{
Easy,
Normal,
Hard,
Insane,
Expert
}
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
{
var rating = beatmap.StarDifficulty;
if (rating < 1.5) return DifficultyRating.Easy;
if (rating < 2.25) return DifficultyRating.Normal;
if (rating < 3.75) return DifficultyRating.Hard;
if (rating < 5.25) return DifficultyRating.Insane;
return DifficultyRating.Expert;
}
private Color4 getColour(BeatmapInfo beatmap)
{
switch (getDifficultyRating(beatmap))
{
case DifficultyRating.Easy:
return palette.Green;
default:
case DifficultyRating.Normal:
return palette.Yellow;
case DifficultyRating.Hard:
return palette.Pink;
case DifficultyRating.Insane:
return palette.Purple;
case DifficultyRating.Expert:
return palette.Gray0;
}
}
}
}

View File

@ -3,7 +3,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Database;
using osu.Game.Graphics;
using OpenTK;
@ -11,23 +10,20 @@ using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
internal class DifficultyIcon : Container
internal class DifficultyIcon : DifficultyColouredContainer
{
private readonly BeatmapInfo beatmap;
private OsuColour palette;
public DifficultyIcon(BeatmapInfo beatmap)
public DifficultyIcon(BeatmapInfo beatmap) : base(beatmap)
{
this.beatmap = beatmap;
const float size = 20;
Size = new Vector2(size);
Size = new Vector2(20);
}
[BackgroundDependencyLoader]
private void load(OsuColour palette)
private void load()
{
this.palette = palette;
Children = new[]
{
new TextAwesome
@ -35,7 +31,7 @@ namespace osu.Game.Beatmaps.Drawables
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = Size.X,
Colour = getColour(beatmap),
Colour = AccentColour,
Icon = FontAwesome.fa_circle
},
new TextAwesome
@ -48,43 +44,5 @@ namespace osu.Game.Beatmaps.Drawables
}
};
}
private enum DifficultyRating
{
Easy,
Normal,
Hard,
Insane,
Expert
}
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
{
var rating = beatmap.StarDifficulty;
if (rating < 1.5) return DifficultyRating.Easy;
if (rating < 2.25) return DifficultyRating.Normal;
if (rating < 3.75) return DifficultyRating.Hard;
if (rating < 5.25) return DifficultyRating.Insane;
return DifficultyRating.Expert;
}
private Color4 getColour(BeatmapInfo beatmap)
{
switch (getDifficultyRating(beatmap))
{
case DifficultyRating.Easy:
return palette.Green;
default:
case DifficultyRating.Normal:
return palette.Yellow;
case DifficultyRating.Hard:
return palette.Pink;
case DifficultyRating.Insane:
return palette.Purple;
case DifficultyRating.Expert:
return palette.Gray0;
}
}
}
}
}

View File

@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps.Formats
// TODO: Not sure how far back to go, or differences between versions
}
private HitObjectParser parser;
private ConvertHitObjectParser parser;
private LegacySampleBank defaultSampleBank;
private int defaultSampleVolume = 100;
@ -90,16 +90,16 @@ namespace osu.Game.Beatmaps.Formats
switch (beatmap.BeatmapInfo.RulesetID)
{
case 0:
parser = new Rulesets.Objects.Legacy.Osu.HitObjectParser();
parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
break;
case 1:
parser = new Rulesets.Objects.Legacy.Taiko.HitObjectParser();
parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser();
break;
case 2:
parser = new Rulesets.Objects.Legacy.Catch.HitObjectParser();
parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser();
break;
case 3:
parser = new Rulesets.Objects.Legacy.Mania.HitObjectParser();
parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser();
break;
}
break;

View File

@ -4,6 +4,7 @@
using System;
using osu.Framework.Configuration;
using osu.Framework.Platform;
using osu.Game.Screens.Select;
namespace osu.Game.Configuration
{
@ -34,8 +35,7 @@ namespace osu.Game.Configuration
Set(OsuConfig.MenuParallax, true);
Set(OsuConfig.MenuVoice, true);
Set(OsuConfig.MenuMusic, true);
Set(OsuConfig.BeatmapDetailTab, BeatmapDetailTab.Details);
Set(OsuConfig.ShowInterface, true);
Set(OsuConfig.KeyOverlay, false);
@ -148,6 +148,8 @@ namespace osu.Game.Configuration
Set(OsuConfig.YahooIntegration, false).Disabled = true;
Set(OsuConfig.ForceFrameFlush, false).Disabled = true;
Set(OsuConfig.DetectPerformanceIssues, true).Disabled = true;
Set(OsuConfig.MenuMusic, true).Disabled = true;
Set(OsuConfig.MenuVoice, true).Disabled = true;
Set(OsuConfig.RawInput, false).Disabled = true;
Set(OsuConfig.AbsoluteToOsuWindow, Get<bool>(OsuConfig.RawInput)).Disabled = true;
Set(OsuConfig.ShowMenuTips, true).Disabled = true;
@ -317,6 +319,7 @@ namespace osu.Game.Configuration
MenuMusic,
MenuVoice,
MenuParallax,
BeatmapDetailTab,
RawInput,
AbsoluteToOsuWindow,
ConfineMouse,

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using Newtonsoft.Json;
namespace osu.Game.Database
{
@ -18,11 +19,13 @@ namespace osu.Game.Database
/// <summary>
/// Points of failure on a relative time scale (usually 0..100).
/// </summary>
[JsonProperty(@"fail")]
public IEnumerable<int> Fails { get; set; }
/// <summary>
/// Points of retry on a relative time scale (usually 0..100).
/// </summary>
[JsonProperty(@"exit")]
public IEnumerable<int> Retries { get; set; }
}
}

View File

@ -0,0 +1,46 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
using osu.Game.Database;
namespace osu.Game.Online.API.Requests
{
public class GetBeatmapDetailsRequest : APIRequest<GetBeatmapDeatilsResponse>
{
private readonly BeatmapInfo beatmap;
private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={beatmap.Path}";
public GetBeatmapDetailsRequest(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
}
protected override string Target => $@"beatmaps/{lookupString}";
}
public class GetBeatmapDeatilsResponse : BeatmapMetrics
{
//the online API returns some metrics as a nested object.
[JsonProperty(@"failtimes")]
private BeatmapMetrics failTimes
{
set
{
Fails = value.Fails;
Retries = value.Retries;
}
}
//and other metrics in the beatmap set.
[JsonProperty(@"beatmapset")]
private BeatmapMetrics beatmapSet
{
set
{
Ratings = value.Ratings;
}
}
}
}

View File

@ -294,7 +294,7 @@ namespace osu.Game.Overlays
trackManager.SetExclusive(current.Track);
current.Track.Start();
beatmapSource.Value = current;
}).ContinueWith(task => Schedule(() => task.ThrowIfFaulted()), TaskContinuationOptions.OnlyOnFaulted);
}).ContinueWith(task => Schedule(task.ThrowIfFaulted), TaskContinuationOptions.OnlyOnFaulted);
updateDisplay(current, isNext ? TransformDirection.Next : TransformDirection.Prev);
}

View File

@ -1,49 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Objects
{
public class CurvedHitObject : HitObject, IHasCurve
{
public SliderCurve Curve { get; } = new SliderCurve();
public int RepeatCount { get; set; } = 1;
public double EndTime => 0;
public double Duration => 0;
public List<Vector2> ControlPoints
{
get { return Curve.ControlPoints; }
set { Curve.ControlPoints = value; }
}
public CurveType CurveType
{
get { return Curve.CurveType; }
set { Curve.CurveType = value; }
}
public double Distance
{
get { return Curve.Distance; }
set { Curve.Distance = value; }
}
public Vector2 PositionAt(double progress) => Curve.PositionAt(ProgressAt(progress));
public double ProgressAt(double progress)
{
var p = progress * RepeatCount % 1;
if (RepeatAt(progress) % 2 == 1)
p = 1 - p;
return p;
}
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
}
}

View File

@ -4,6 +4,7 @@
using osu.Game.Audio;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Objects
@ -19,10 +20,14 @@ namespace osu.Game.Rulesets.Objects
/// <summary>
/// The time at which the HitObject starts.
/// </summary>
public double StartTime { get; set; }
public double StartTime;
/// <summary>
/// The samples to be played when this hit object is hit.
/// <para>
/// In the case of <see cref="IHasRepeats"/> types, this is the sample of the curve body
/// and can be treated as the default samples for the hit object.
/// </para>
/// </summary>
public List<SampleInfo> Samples = new List<SampleInfo>();
@ -36,17 +41,24 @@ namespace osu.Game.Rulesets.Objects
ControlPoint overridePoint;
ControlPoint timingPoint = timing.TimingPointAt(StartTime, out overridePoint);
foreach (var sample in Samples)
{
if (sample.Volume == 0)
sample.Volume = (overridePoint ?? timingPoint)?.SampleVolume ?? 0;
ControlPoint samplePoint = overridePoint ?? timingPoint;
// If the bank is not assigned a name, assign it from the control point
if (!string.IsNullOrEmpty(sample.Bank))
continue;
// Initialize first sample
Samples.ForEach(s => initializeSampleInfo(s, samplePoint));
sample.Bank = (overridePoint ?? timingPoint)?.SampleBank ?? @"normal";
}
// Initialize any repeat samples
var repeatData = this as IHasRepeats;
repeatData?.RepeatSamples?.ForEach(r => r.ForEach(s => initializeSampleInfo(s, samplePoint)));
}
private void initializeSampleInfo(SampleInfo sample, ControlPoint controlPoint)
{
if (sample.Volume == 0)
sample.Volume = controlPoint?.SampleVolume ?? 0;
// If the bank is not assigned a name, assign it from the control point
if (string.IsNullOrEmpty(sample.Bank))
sample.Bank = controlPoint?.SampleBank ?? @"normal";
}
}
}

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
/// <summary>
/// Legacy osu!catch Hit-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Hit : HitObject, IHasCombo, IHasXPosition
internal sealed class ConvertHit : HitObject, IHasCombo, IHasXPosition
{
public float X { get; set; }

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
@ -10,33 +11,34 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
/// <summary>
/// A HitObjectParser to parse legacy osu!catch Beatmaps.
/// </summary>
internal class HitObjectParser : Legacy.HitObjectParser
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
protected override HitObject CreateHit(Vector2 position, bool newCombo)
{
return new Hit
return new ConvertHit
{
X = position.X,
NewCombo = newCombo,
};
}
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount)
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
{
return new Slider
return new ConvertSlider
{
X = position.X,
NewCombo = newCombo,
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
RepeatSamples = repeatSamples,
RepeatCount = repeatCount
};
}
protected override HitObject CreateSpinner(Vector2 position, double endTime)
{
return new Spinner
return new ConvertSpinner
{
EndTime = endTime
};

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
/// <summary>
/// Legacy osu!catch Slider-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Slider : CurvedHitObject, IHasXPosition, IHasCombo
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo
{
public float X { get; set; }

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
/// <summary>
/// Legacy osu!catch Spinner-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Spinner : HitObject, IHasEndTime
internal sealed class ConvertSpinner : HitObject, IHasEndTime
{
public double EndTime { get; set; }

View File

@ -14,29 +14,28 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <summary>
/// A HitObjectParser to parse legacy Beatmaps.
/// </summary>
internal abstract class HitObjectParser : Objects.HitObjectParser
internal abstract class ConvertHitObjectParser : HitObjectParser
{
public override HitObject Parse(string text)
{
string[] split = text.Split(',');
var type = (HitObjectType)int.Parse(split[3]) & ~HitObjectType.ColourHax;
bool combo = type.HasFlag(HitObjectType.NewCombo);
type &= ~HitObjectType.NewCombo;
ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]) & ~ConvertHitObjectType.ColourHax;
bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
type &= ~ConvertHitObjectType.NewCombo;
int sampleVolume = 0;
string normalSampleBank = null;
string addSampleBank = null;
var soundType = (LegacySoundType)int.Parse(split[4]);
var bankInfo = new SampleBankInfo();
HitObject result;
if ((type & HitObjectType.Circle) > 0)
if ((type & ConvertHitObjectType.Circle) > 0)
{
result = CreateHit(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo);
if (split.Length > 5)
readCustomSampleBanks(split[5], ref normalSampleBank, ref addSampleBank, ref sampleVolume);
readCustomSampleBanks(split[5], bankInfo);
}
else if ((type & HitObjectType.Slider) > 0)
else if ((type & ConvertHitObjectType.Slider) > 0)
{
CurveType curveType = CurveType.Catmull;
double length = 0;
@ -77,26 +76,74 @@ namespace osu.Game.Rulesets.Objects.Legacy
if (split.Length > 7)
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount);
if (split.Length > 10)
readCustomSampleBanks(split[10], ref normalSampleBank, ref addSampleBank, ref sampleVolume);
readCustomSampleBanks(split[10], bankInfo);
// One node for each repeat + the start and end nodes
// Note that the first length of the slider is considered a repeat, but there are no actual repeats happening
int nodes = Math.Max(0, repeatCount - 1) + 2;
// Populate node sample bank infos with the default hit object sample bank
var nodeBankInfos = new List<SampleBankInfo>();
for (int i = 0; i < nodes; i++)
nodeBankInfos.Add(bankInfo.Clone());
// Read any per-node sample banks
if (split.Length > 9 && split[9].Length > 0)
{
string[] sets = split[9].Split('|');
for (int i = 0; i < nodes; i++)
{
if (i >= sets.Length)
break;
SampleBankInfo info = nodeBankInfos[i];
readCustomSampleBanks(sets[i], info);
}
}
// Populate node sound types with the default hit object sound type
var nodeSoundTypes = new List<LegacySoundType>();
for (int i = 0; i < nodes; i++)
nodeSoundTypes.Add(soundType);
// Read any per-node sound types
if (split.Length > 8 && split[8].Length > 0)
{
string[] adds = split[8].Split('|');
for (int i = 0; i < nodes; i++)
{
if (i >= adds.Length)
break;
int sound;
int.TryParse(adds[i], out sound);
nodeSoundTypes[i] = (LegacySoundType)sound;
}
}
// Generate the final per-node samples
var nodeSamples = new List<List<SampleInfo>>(nodes);
for (int i = 0; i <= repeatCount; i++)
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount, nodeSamples);
}
else if ((type & HitObjectType.Spinner) > 0)
else if ((type & ConvertHitObjectType.Spinner) > 0)
{
result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture));
if (split.Length > 6)
readCustomSampleBanks(split[6], ref normalSampleBank, ref addSampleBank, ref sampleVolume);
readCustomSampleBanks(split[6], bankInfo);
}
else if ((type & HitObjectType.Hold) > 0)
else if ((type & ConvertHitObjectType.Hold) > 0)
{
// Note: Hold is generated by BMS converts
// Todo: Apparently end time is determined by samples??
// Shouldn't need implementation until mania
result = new Hold
result = new ConvertHold
{
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])),
NewCombo = combo
@ -106,50 +153,12 @@ namespace osu.Game.Rulesets.Objects.Legacy
throw new InvalidOperationException($@"Unknown hit object type {type}");
result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture);
var soundType = (LegacySoundType)int.Parse(split[4]);
result.Samples.Add(new SampleInfo
{
Bank = normalSampleBank,
Name = SampleInfo.HIT_NORMAL,
Volume = sampleVolume
});
if ((soundType & LegacySoundType.Finish) > 0)
{
result.Samples.Add(new SampleInfo
{
Bank = addSampleBank,
Name = SampleInfo.HIT_FINISH,
Volume = sampleVolume
});
}
if ((soundType & LegacySoundType.Whistle) > 0)
{
result.Samples.Add(new SampleInfo
{
Bank = addSampleBank,
Name = SampleInfo.HIT_WHISTLE,
Volume = sampleVolume
});
}
if ((soundType & LegacySoundType.Clap) > 0)
{
result.Samples.Add(new SampleInfo
{
Bank = addSampleBank,
Name = SampleInfo.HIT_CLAP,
Volume = sampleVolume
});
}
result.Samples = convertSoundType(soundType, bankInfo);
return result;
}
private void readCustomSampleBanks(string str, ref string normalSampleBank, ref string addSampleBank, ref int sampleVolume)
private void readCustomSampleBanks(string str, SampleBankInfo bankInfo)
{
if (string.IsNullOrEmpty(str))
return;
@ -169,9 +178,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
if (stringAddBank == @"none")
stringAddBank = null;
normalSampleBank = stringBank;
addSampleBank = stringAddBank;
sampleVolume = split.Length > 3 ? int.Parse(split[3]) : 0;
bankInfo.Normal = stringBank;
bankInfo.Add = stringAddBank;
if (split.Length > 3)
bankInfo.Volume = int.Parse(split[3]);
}
/// <summary>
@ -191,8 +202,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <param name="length">The slider length.</param>
/// <param name="curveType">The slider curve type.</param>
/// <param name="repeatCount">The slider repeat count.</param>
/// <param name="repeatSamples">The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider.</param>
/// <returns>The hit object.</returns>
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount);
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples);
/// <summary>
/// Creates a legacy Spinner-type hit object.
@ -202,6 +214,63 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <returns>The hit object.</returns>
protected abstract HitObject CreateSpinner(Vector2 position, double endTime);
private List<SampleInfo> convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
{
var soundTypes = new List<SampleInfo>
{
new SampleInfo
{
Bank = bankInfo.Normal,
Name = SampleInfo.HIT_NORMAL,
Volume = bankInfo.Volume
}
};
if ((type & LegacySoundType.Finish) > 0)
{
soundTypes.Add(new SampleInfo
{
Bank = bankInfo.Add,
Name = SampleInfo.HIT_FINISH,
Volume = bankInfo.Volume
});
}
if ((type & LegacySoundType.Whistle) > 0)
{
soundTypes.Add(new SampleInfo
{
Bank = bankInfo.Add,
Name = SampleInfo.HIT_WHISTLE,
Volume = bankInfo.Volume
});
}
if ((type & LegacySoundType.Clap) > 0)
{
soundTypes.Add(new SampleInfo
{
Bank = bankInfo.Add,
Name = SampleInfo.HIT_CLAP,
Volume = bankInfo.Volume
});
}
return soundTypes;
}
private class SampleBankInfo
{
public string Normal;
public string Add;
public int Volume;
public SampleBankInfo Clone()
{
return (SampleBankInfo)MemberwiseClone();
}
}
[Flags]
private enum LegacySoundType
{

View File

@ -6,7 +6,7 @@ using System;
namespace osu.Game.Rulesets.Objects.Legacy
{
[Flags]
public enum HitObjectType
internal enum ConvertHitObjectType
{
Circle = 1 << 0,
Slider = 1 << 1,

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <summary>
/// Legacy Hold-type, used for parsing "specials" in beatmaps.
/// </summary>
internal sealed class Hold : HitObject, IHasPosition, IHasCombo, IHasHold
internal sealed class ConvertHold : HitObject, IHasPosition, IHasCombo, IHasHold
{
public Vector2 Position { get; set; }

View File

@ -0,0 +1,39 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Types;
using System;
using System.Collections.Generic;
using OpenTK;
using osu.Game.Audio;
namespace osu.Game.Rulesets.Objects.Legacy
{
internal abstract class ConvertSlider : HitObject, IHasCurve
{
public List<Vector2> ControlPoints { get; set; }
public CurveType CurveType { get; set; }
public double Distance { get; set; }
public List<List<SampleInfo>> RepeatSamples { get; set; }
public int RepeatCount { get; set; } = 1;
public double EndTime { get; set; }
public double Duration { get; set; }
public Vector2 PositionAt(double progress)
{
throw new NotImplementedException();
}
public double ProgressAt(double progress)
{
throw new NotImplementedException();
}
public int RepeatAt(double progress)
{
throw new NotImplementedException();
}
}
}

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
/// <summary>
/// Legacy osu!mania Hit-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Hit : HitObject, IHasXPosition, IHasCombo
internal sealed class ConvertHit : HitObject, IHasXPosition, IHasCombo
{
public float X { get; set; }

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
@ -10,33 +11,34 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
/// <summary>
/// A HitObjectParser to parse legacy osu!mania Beatmaps.
/// </summary>
internal class HitObjectParser : Legacy.HitObjectParser
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
protected override HitObject CreateHit(Vector2 position, bool newCombo)
{
return new Hit
return new ConvertHit
{
X = position.X,
NewCombo = newCombo,
};
}
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount)
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
{
return new Slider
return new ConvertSlider
{
X = position.X,
NewCombo = newCombo,
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
RepeatSamples = repeatSamples,
RepeatCount = repeatCount
};
}
protected override HitObject CreateSpinner(Vector2 position, double endTime)
{
return new Spinner
return new ConvertSpinner
{
X = position.X,
EndTime = endTime

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
/// <summary>
/// Legacy osu!mania Slider-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Slider : CurvedHitObject, IHasXPosition, IHasCombo
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo
{
public float X { get; set; }

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
/// <summary>
/// Legacy osu!mania Spinner-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Spinner : HitObject, IHasEndTime, IHasXPosition
internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasXPosition
{
public double EndTime { get; set; }

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
/// <summary>
/// Legacy osu! Hit-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Hit : HitObject, IHasPosition, IHasCombo
internal sealed class ConvertHit : HitObject, IHasPosition, IHasCombo
{
public Vector2 Position { get; set; }

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
@ -10,33 +11,34 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
/// <summary>
/// A HitObjectParser to parse legacy osu! Beatmaps.
/// </summary>
internal class HitObjectParser : Legacy.HitObjectParser
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
protected override HitObject CreateHit(Vector2 position, bool newCombo)
{
return new Hit
return new ConvertHit
{
Position = position,
NewCombo = newCombo,
};
}
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount)
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
{
return new Slider
return new ConvertSlider
{
Position = position,
NewCombo = newCombo,
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
RepeatSamples = repeatSamples,
RepeatCount = repeatCount
};
}
protected override HitObject CreateSpinner(Vector2 position, double endTime)
{
return new Spinner
return new ConvertSpinner
{
Position = position,
EndTime = endTime

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
/// <summary>
/// Legacy osu! Slider-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Slider : CurvedHitObject, IHasPosition, IHasCombo
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo
{
public Vector2 Position { get; set; }

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
/// <summary>
/// Legacy osu! Spinner-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Spinner : HitObject, IHasEndTime, IHasPosition
internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasPosition
{
public double EndTime { get; set; }

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
/// <summary>
/// Legacy osu!taiko Hit-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Hit : HitObject, IHasCombo
internal sealed class ConvertHit : HitObject, IHasCombo
{
public bool NewCombo { get; set; }
}

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
@ -10,31 +11,32 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
/// <summary>
/// A HitObjectParser to parse legacy osu!taiko Beatmaps.
/// </summary>
internal class HitObjectParser : Legacy.HitObjectParser
internal class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
protected override HitObject CreateHit(Vector2 position, bool newCombo)
{
return new Hit
return new ConvertHit
{
NewCombo = newCombo,
};
}
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount)
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
{
return new Slider
return new ConvertSlider
{
NewCombo = newCombo,
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
RepeatSamples = repeatSamples,
RepeatCount = repeatCount
};
}
protected override HitObject CreateSpinner(Vector2 position, double endTime)
{
return new Spinner
return new ConvertSpinner
{
EndTime = endTime
};

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
/// <summary>
/// Legacy osu!taiko Slider-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Slider : CurvedHitObject, IHasCombo
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasCombo
{
public bool NewCombo { get; set; }
}

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
/// <summary>
/// Legacy osu!taiko Spinner-type, used for parsing Beatmaps.
/// </summary>
internal sealed class Spinner : HitObject, IHasEndTime
internal sealed class ConvertSpinner : HitObject, IHasEndTime
{
public double EndTime { get; set; }

View File

@ -11,11 +11,6 @@ namespace osu.Game.Rulesets.Objects.Types
/// </summary>
public interface IHasCurve : IHasDistance, IHasRepeats
{
/// <summary>
/// The curve.
/// </summary>
SliderCurve Curve { get; }
/// <summary>
/// The control points that shape the curve.
/// </summary>

View File

@ -1,6 +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 osu.Game.Audio;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Objects.Types
{
/// <summary>
@ -12,5 +15,10 @@ namespace osu.Game.Rulesets.Objects.Types
/// The amount of times the HitObject repeats.
/// </summary>
int RepeatCount { get; }
/// <summary>
/// The samples to be played when each repeat node is hit (0 -> first repeat node, 1 -> second repeat node, etc).
/// </summary>
List<List<SampleInfo>> RepeatSamples { get; }
}
}

View File

@ -38,8 +38,6 @@ namespace osu.Game.Screens.Play
protected override void Update()
{
base.Update();
if (parentClock == null) return;
clock.Rate = parentClock.Rate;

View File

@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select
{
beatmap = value;
Leaderboard.Beatmap = beatmap?.BeatmapInfo;
Details.Beatmap = beatmap?.Beatmap.BeatmapInfo;
Details.Beatmap = beatmap?.BeatmapInfo;
}
}
@ -66,16 +66,16 @@ namespace osu.Game.Screens.Select
{
Details = new BeatmapDetails
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(5),
RelativeSizeAxes = Axes.X,
Masking = true,
Height = 352,
Alpha = 0,
},
Leaderboard = new Leaderboard
{
RelativeSizeAxes = Axes.Both,
}
});
}
}
}
}

View File

@ -4,10 +4,12 @@
using System;
using OpenTK.Graphics;
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.Sprites;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
@ -21,15 +23,22 @@ namespace osu.Game.Screens.Select
public Action<BeatmapDetailTab, bool> OnFilter; //passed the selected tab and if mods is checked
private Bindable<BeatmapDetailTab> selectedTab;
private void invokeOnFilter()
{
OnFilter?.Invoke(tabs.Current, modsCheckbox.Current);
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
private void load(OsuColour colour, OsuConfigManager config)
{
modsCheckbox.AccentColour = tabs.AccentColour = colour.YellowLight;
selectedTab = config.GetBindable<BeatmapDetailTab>(OsuConfig.BeatmapDetailTab);
tabs.Current.BindTo(selectedTab);
tabs.Current.TriggerChange();
}
public BeatmapDetailAreaTabControl()
@ -62,8 +71,6 @@ namespace osu.Game.Screens.Select
tabs.Current.ValueChanged += item => invokeOnFilter();
modsCheckbox.Current.ValueChanged += item => invokeOnFilter();
tabs.Current.Value = BeatmapDetailTab.Global;
}
}

View File

@ -14,6 +14,9 @@ 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;
namespace osu.Game.Screens.Select
{
@ -39,60 +42,108 @@ namespace osu.Game.Screens.Select
private readonly BarGraph retryGraph;
private readonly BarGraph failGraph;
private ScheduledDelegate pendingBeatmapSwitch;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get
{
return beatmap;
}
get { return beatmap; }
set
{
beatmap = value;
if (beatmap == null) return;
description.Text = beatmap.Version;
source.Text = beatmap.Metadata.Source;
tags.Text = beatmap.Metadata.Tags;
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;
if (beatmap.Metrics?.Ratings.Any() ?? false)
{
var ratings = beatmap.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);
}
else
ratingsContainer.Hide();
if ((beatmap.Metrics?.Retries.Any() ?? false) && beatmap.Metrics.Fails.Any())
{
var retries = beatmap.Metrics.Retries;
var fails = beatmap.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));
}
else
retryFailContainer.Hide();
pendingBeatmapSwitch?.Cancel();
pendingBeatmapSwitch = Schedule(updateStats);
}
}
private void updateStats()
{
if (beatmap == null) return;
description.Text = beatmap.Version;
source.Text = beatmap.Metadata.Source;
tags.Text = beatmap.Metadata.Tags;
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;
var requestedBeatmap = beatmap;
if (requestedBeatmap.Metrics == null)
{
var lookup = new GetBeatmapDetailsRequest(requestedBeatmap);
lookup.Success += res =>
{
if (beatmap != requestedBeatmap)
//the beatmap has been changed since we started the lookup.
return;
requestedBeatmap.Metrics = res;
Schedule(() => updateMetrics(res));
};
lookup.Failure += e => updateMetrics(null);
api.Queue(lookup);
}
updateMetrics(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)
{
var hasRatings = metrics?.Ratings.Any() ?? false;
var hasRetriesFails = (metrics?.Retries.Any() ?? false) && metrics.Fails.Any();
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, EasingTypes.Out);
}
else if (failOnMissing)
ratingsGraph.Values = new float[10];
else
ratingsContainer.FadeColour(Color4.Gray, 500, EasingTypes.Out);
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, EasingTypes.Out);
}
else if (failOnMissing)
{
failGraph.Values = new float[100];
retryGraph.Values = new float[100];
}
else
retryFailContainer.FadeColour(Color4.Gray, 500, EasingTypes.Out);
}
public BeatmapDetails()
{
Children = new Drawable[]
@ -113,7 +164,6 @@ namespace osu.Game.Screens.Select
Direction = FillDirection.Vertical,
LayoutDuration = 200,
LayoutEasing = EasingTypes.OutQuint,
Padding = new MarginPadding(10) { Top = 25 },
Children = new []
{
description = new MetadataSegment("Description"),
@ -148,8 +198,8 @@ namespace osu.Game.Screens.Select
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0,10),
Padding = new MarginPadding(15) { Top = 25 },
Spacing = new Vector2(0,5),
Padding = new MarginPadding(10),
Children = new []
{
circleSize = new DifficultyRow("Circle Size", 7),
@ -252,7 +302,7 @@ namespace osu.Game.Screens.Select
new Container<BarGraph>
{
RelativeSizeAxes = Axes.X,
Size = new Vector2(1/0.6f, 50),
Size = new Vector2(1 / 0.6f, 50),
Children = new[]
{
retryGraph = new BarGraph
@ -272,9 +322,13 @@ namespace osu.Game.Screens.Select
};
}
private APIAccess api;
[BackgroundDependencyLoader]
private void load(OsuColour colour)
private void load(OsuColour colour, APIAccess api)
{
this.api = api;
description.AccentColour = colour.GrayB;
source.AccentColour = colour.GrayB;
tags.AccentColour = colour.YellowLight;
@ -308,7 +362,7 @@ namespace osu.Game.Screens.Select
{
difficultyValue = value;
bar.Length = value / maxValue;
valueText.Text = value.ToString(CultureInfo.InvariantCulture);
valueText.Text = value.ToString("N1", CultureInfo.CurrentCulture);
}
}
@ -431,4 +485,4 @@ namespace osu.Game.Screens.Select
}
}
}
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@ -148,11 +149,16 @@ namespace osu.Game.Screens.Select
},
},
},
// Text for beatmap info
new DifficultyColourBar(beatmap.BeatmapInfo)
{
RelativeSizeAxes = Axes.Y,
Width = 20,
},
new FillFlowContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Name = "Top-aligned metadata",
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 10, Left = 25, Right = 10, Bottom = 20 },
AutoSizeAxes = Axes.Both,
@ -161,16 +167,32 @@ namespace osu.Game.Screens.Select
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = metadata.Artist + " -- " + metadata.Title,
Text = beatmapInfo.Version,
TextSize = 24,
},
}
},
new FillFlowContainer
{
Name = "Bottom-aligned metadata",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 15, Left = 25, Right = 10, Bottom = 20 },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = !string.IsNullOrEmpty(metadata.Source) ? metadata.Source + " — " + metadata.Title : metadata.Title,
TextSize = 28,
Shadow = true,
},
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = beatmapInfo.Version,
Text = metadata.Artist,
TextSize = 17,
Shadow = true,
},
new FillFlowContainer
{
@ -184,20 +206,18 @@ namespace osu.Game.Screens.Select
Font = @"Exo2.0-Medium",
Text = "mapped by ",
TextSize = 15,
Shadow = true,
},
},
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = metadata.Author,
TextSize = 15,
Shadow = true,
},
},
}
},
new FillFlowContainer
{
Margin = new MarginPadding { Top = 20 },
Margin = new MarginPadding { Top = 20, Left = 10 },
Spacing = new Vector2(40, 0),
AutoSizeAxes = Axes.Both,
Children = labels
@ -256,5 +276,37 @@ namespace osu.Game.Screens.Select
};
}
}
private class DifficultyColourBar : DifficultyColouredContainer
{
public DifficultyColourBar(BeatmapInfo beatmap) : base(beatmap)
{
}
[BackgroundDependencyLoader]
private void load()
{
const float full_opacity_ratio = 0.7f;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = AccentColour,
Width = full_opacity_ratio,
},
new Box
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = AccentColour,
Alpha = 0.5f,
X = full_opacity_ratio,
Width = 1 - full_opacity_ratio,
}
};
}
}
}
}

View File

@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using System;
using osu.Framework.Allocation;
using osu.Framework.Threading;
using osu.Game.Database;
using osu.Game.Rulesets.Scoring;
using osu.Game.Online.API;
@ -93,13 +94,18 @@ namespace osu.Game.Screens.Select.Leaderboards
private BeatmapInfo beatmap;
private ScheduledDelegate pendingBeatmapSwitch;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
beatmap = value;
Schedule(updateScores);
Scores = null;
pendingBeatmapSwitch?.Cancel();
pendingBeatmapSwitch = Schedule(updateScores);
}
}

View File

@ -109,6 +109,20 @@
<Compile Include="IO\Serialization\IJsonSerializable.cs" />
<Compile Include="IPC\ScoreIPCChannel.cs" />
<Compile Include="Rulesets\BeatmapStatistic.cs" />
<Compile Include="Rulesets\Objects\Legacy\Catch\ConvertHit.cs" />
<Compile Include="Rulesets\Objects\Legacy\Catch\ConvertHitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\Catch\ConvertSlider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Catch\ConvertSpinner.cs" />
<Compile Include="Rulesets\Objects\Legacy\ConvertSlider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\ConvertHit.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\ConvertHitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\ConvertSlider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\ConvertSpinner.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\ConvertHitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\Taiko\ConvertHit.cs" />
<Compile Include="Rulesets\Objects\Legacy\Taiko\ConvertHitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\Taiko\ConvertSlider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Taiko\ConvertSpinner.cs" />
<Compile Include="Rulesets\Mods\IApplicableToClock.cs" />
<Compile Include="Rulesets\Mods\ModAutoplay.cs" />
<Compile Include="Rulesets\Mods\ModCinema.cs" />
@ -124,19 +138,6 @@
<Compile Include="Rulesets\Mods\ModRelax.cs" />
<Compile Include="Rulesets\Mods\ModSuddenDeath.cs" />
<Compile Include="Rulesets\Mods\MultiMod.cs" />
<Compile Include="Rulesets\Objects\Legacy\Catch\Hit.cs" />
<Compile Include="Rulesets\Objects\Legacy\Catch\HitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\Catch\Slider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Catch\Spinner.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\Hit.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\HitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\Slider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\Spinner.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\HitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\Taiko\Hit.cs" />
<Compile Include="Rulesets\Objects\Legacy\Taiko\HitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\Taiko\Slider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Taiko\Spinner.cs" />
<Compile Include="Rulesets\Objects\Types\IHasXPosition.cs" />
<Compile Include="Rulesets\Objects\Types\IHasYPosition.cs" />
<Compile Include="Rulesets\Replays\Replay.cs" />
@ -149,12 +150,11 @@
<Compile Include="Rulesets\Objects\Drawables\HitResult.cs" />
<Compile Include="Rulesets\Objects\BezierApproximator.cs" />
<Compile Include="Rulesets\Objects\CircularArcApproximator.cs" />
<Compile Include="Rulesets\Objects\CurvedHitObject.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\Hit.cs" />
<Compile Include="Rulesets\Objects\Legacy\HitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\Hold.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\Slider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\Spinner.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\ConvertHit.cs" />
<Compile Include="Rulesets\Objects\Legacy\ConvertHitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\ConvertHold.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\ConvertSlider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\ConvertSpinner.cs" />
<Compile Include="Rulesets\Objects\SliderCurve.cs" />
<Compile Include="Rulesets\Objects\Types\CurveType.cs" />
<Compile Include="Rulesets\Objects\Drawables\IDrawableHitObjectWithProxiedApproach.cs" />
@ -167,7 +167,7 @@
<Compile Include="Rulesets\Objects\Types\IHasRepeats.cs" />
<Compile Include="Rulesets\Objects\Types\IHasPosition.cs" />
<Compile Include="Rulesets\Objects\Types\IHasHold.cs" />
<Compile Include="Rulesets\Objects\Legacy\HitObjectType.cs" />
<Compile Include="Rulesets\Objects\Legacy\ConvertHitObjectType.cs" />
<Compile Include="Rulesets\Replays\ReplayButtonState.cs" />
<Compile Include="Rulesets\Replays\ReplayFrame.cs" />
<Compile Include="Database\RulesetDatabase.cs" />
@ -179,6 +179,7 @@
<Compile Include="Rulesets\UI\StandardHudOverlay.cs" />
<Compile Include="Online\API\IOnlineComponent.cs" />
<Compile Include="Online\API\Requests\GetScoresRequest.cs" />
<Compile Include="Online\API\Requests\GetBeatmapDetailsRequest.cs" />
<Compile Include="Online\API\Requests\GetUserRequest.cs" />
<Compile Include="Overlays\DragBar.cs" />
<Compile Include="Overlays\LoginOverlay.cs" />
@ -187,6 +188,7 @@
<Compile Include="Beatmaps\WorkingBeatmap.cs" />
<Compile Include="Beatmaps\Drawables\BeatmapSetHeader.cs" />
<Compile Include="Beatmaps\Drawables\DifficultyIcon.cs" />
<Compile Include="Beatmaps\Drawables\DifficultyColouredContainer.cs" />
<Compile Include="Beatmaps\Drawables\Panel.cs" />
<Compile Include="Rulesets\Objects\Drawables\DrawableHitObject.cs" />
<Compile Include="Rulesets\Objects\HitObject.cs" />