1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-15 14:47:18 +08:00

Merge branch 'master' into update_tab_item_font

This commit is contained in:
Dean Herbert 2019-01-15 18:31:16 +09:00 committed by GitHub
commit 7ef127a3bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 964 additions and 527 deletions

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework; using osu.Framework;
@ -77,10 +76,10 @@ namespace osu.Desktop.Updater
switch (RuntimeInfo.OS) switch (RuntimeInfo.OS)
{ {
case RuntimeInfo.Platform.Windows: case RuntimeInfo.Platform.Windows:
bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".exe")); bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".exe"));
break; break;
case RuntimeInfo.Platform.MacOsx: case RuntimeInfo.Platform.MacOsx:
bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".app.zip")); bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".app.zip"));
break; break;
} }

View File

@ -26,7 +26,6 @@ namespace osu.Game.Rulesets.Catch.Tests
} }
}; };
for (int i = 0; i < 512; i++) for (int i = 0; i < 512; i++)
beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 }); beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });

View File

@ -19,7 +19,6 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } }; var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
for (int i = 0; i < 512; i++) for (int i = 0; i < 512; i++)
if (i % 5 < 3) if (i % 5 < 3)
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });

View File

@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty
{ {
public class CatchDifficultyCalculator : DifficultyCalculator public class CatchDifficultyCalculator : DifficultyCalculator
{ {
/// <summary> /// <summary>
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.

View File

@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
base.SkinChanged(skin, allowFallback); base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo) if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
} }
private const float preempt = 1000; private const float preempt = 1000;

View File

@ -180,7 +180,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
foreach (var obj in newPattern.HitObjects) foreach (var obj in newPattern.HitObjects)
yield return obj; yield return obj;
} }
} }

View File

@ -53,7 +53,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
if (!calculateStrainValues(difficultyHitObjects, timeRate)) if (!calculateStrainValues(difficultyHitObjects, timeRate))
return new DifficultyAttributes(mods, 0); return new DifficultyAttributes(mods, 0);
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor; double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
return new ManiaDifficultyAttributes(mods, starRating) return new ManiaDifficultyAttributes(mods, starRating)

View File

@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Mania.Objects
private readonly int beatmapColumnCount; private readonly int beatmapColumnCount;
private readonly double endTime; private readonly double endTime;
private readonly double[] heldUntil; private readonly double[] heldUntil;

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -23,60 +24,70 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
var osuBeatmap = (Beatmap<OsuHitObject>)Beatmap; var osuBeatmap = (Beatmap<OsuHitObject>)Beatmap;
// Reset stacking if (osuBeatmap.HitObjects.Count > 0)
foreach (var h in osuBeatmap.HitObjects) {
h.StackHeight = 0; // Reset stacking
foreach (var h in osuBeatmap.HitObjects)
h.StackHeight = 0;
if (Beatmap.BeatmapInfo.BeatmapVersion >= 6) if (Beatmap.BeatmapInfo.BeatmapVersion >= 6)
applyStacking(osuBeatmap); applyStacking(osuBeatmap, 0, osuBeatmap.HitObjects.Count - 1);
else else
applyStackingOld(osuBeatmap); applyStackingOld(osuBeatmap);
}
} }
private void applyStacking(Beatmap<OsuHitObject> beatmap) private void applyStacking(Beatmap<OsuHitObject> beatmap, int startIndex, int endIndex)
{ {
// Extend the end index to include objects they are stacked on if (startIndex > endIndex) throw new ArgumentOutOfRangeException(nameof(startIndex), $"{nameof(startIndex)} cannot be greater than {nameof(endIndex)}.");
int extendedEndIndex = beatmap.HitObjects.Count - 1; if (startIndex < 0) throw new ArgumentOutOfRangeException(nameof(startIndex), $"{nameof(startIndex)} cannot be less than 0.");
for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--) if (endIndex < 0) throw new ArgumentOutOfRangeException(nameof(endIndex), $"{nameof(endIndex)} cannot be less than 0.");
int extendedEndIndex = endIndex;
if (endIndex < beatmap.HitObjects.Count - 1)
{ {
int stackBaseIndex = i; // Extend the end index to include objects they are stacked on
for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++) for (int i = endIndex; i >= startIndex; i--)
{ {
OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex]; int stackBaseIndex = i;
if (stackBaseObject is Spinner) break; for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++)
OsuHitObject objectN = beatmap.HitObjects[n];
if (objectN is Spinner)
continue;
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency;
if (objectN.StartTime - endTime > stackThreshold)
//We are no longer within stacking range of the next object.
break;
if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
{ {
stackBaseIndex = n; OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex];
if (stackBaseObject is Spinner) break;
// HitObjects after the specified update range haven't been reset yet OsuHitObject objectN = beatmap.HitObjects[n];
objectN.StackHeight = 0; if (objectN is Spinner)
continue;
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency;
if (objectN.StartTime - endTime > stackThreshold)
//We are no longer within stacking range of the next object.
break;
if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance
|| stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
{
stackBaseIndex = n;
// HitObjects after the specified update range haven't been reset yet
objectN.StackHeight = 0;
}
} }
}
if (stackBaseIndex > extendedEndIndex) if (stackBaseIndex > extendedEndIndex)
{ {
extendedEndIndex = stackBaseIndex; extendedEndIndex = stackBaseIndex;
if (extendedEndIndex == beatmap.HitObjects.Count - 1) if (extendedEndIndex == beatmap.HitObjects.Count - 1)
break; break;
}
} }
} }
//Reverse pass for stack calculation. //Reverse pass for stack calculation.
int extendedStartIndex = 0; int extendedStartIndex = startIndex;
for (int i = extendedEndIndex; i > 0; i--) for (int i = extendedEndIndex; i > startIndex; i--)
{ {
int n = i; int n = i;
/* We should check every note which has not yet got a stack. /* We should check every note which has not yet got a stack.
@ -155,7 +166,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
/* We have hit the first slider in a possible stack. /* We have hit the first slider in a possible stack.
* From this point on, we ALWAYS stack positive regardless. * From this point on, we ALWAYS stack positive regardless.
*/ */
while (--n >= 0) while (--n >= startIndex)
{ {
OsuHitObject objectN = beatmap.HitObjects[n]; OsuHitObject objectN = beatmap.HitObjects[n];
if (objectN is Spinner) continue; if (objectN is Spinner) continue;

View File

@ -105,13 +105,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double approachRateFactor = 1.0f; double approachRateFactor = 1.0f;
if (Attributes.ApproachRate > 10.33f) if (Attributes.ApproachRate > 10.33f)
approachRateFactor += 0.45f * (Attributes.ApproachRate - 10.33f); approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
else if (Attributes.ApproachRate < 8.0f) else if (Attributes.ApproachRate < 8.0f)
{ {
// HD is worth more with lower ar!
if (mods.Any(h => h is OsuModHidden))
approachRateFactor += 0.02f * (8.0f - Attributes.ApproachRate);
else
approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate); approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate);
} }
@ -119,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
if (mods.Any(h => h is OsuModHidden)) if (mods.Any(h => h is OsuModHidden))
aimValue *= 1.02 + (11.0f - Attributes.ApproachRate) / 50.0; // Gives a 1.04 bonus for AR10, a 1.06 bonus for AR9, a 1.02 bonus for AR11. aimValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
if (mods.Any(h => h is OsuModFlashlight)) if (mods.Any(h => h is OsuModFlashlight))
{ {
@ -152,13 +148,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (beatmapMaxCombo > 0) if (beatmapMaxCombo > 0)
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
double approachRateFactor = 1.0f;
if (Attributes.ApproachRate > 10.33f)
approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
speedValue *= approachRateFactor;
if (mods.Any(m => m is OsuModHidden)) if (mods.Any(m => m is OsuModHidden))
speedValue *= 1.18f; speedValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
// Scale the speed value with accuracy _slightly_ // Scale the speed value with accuracy _slightly_
speedValue *= 0.5f + accuracy / 2.0f; speedValue *= 0.02f + accuracy;
// It is important to also consider accuracy difficulty when doing that // It is important to also consider accuracy difficulty when doing that
speedValue *= 0.98f + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; speedValue *= 0.96f + Math.Pow(Attributes.OverallDifficulty, 2) / 1600;
return speedValue; return speedValue;
} }
@ -186,7 +188,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f)); accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
if (mods.Any(m => m is OsuModHidden)) if (mods.Any(m => m is OsuModHidden))
accuracyValue *= 1.02f; accuracyValue *= 1.08f;
if (mods.Any(m => m is OsuModFlashlight)) if (mods.Any(m => m is OsuModFlashlight))
accuracyValue *= 1.02f; accuracyValue *= 1.02f;

View File

@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.SkinChanged(skin, allowFallback); base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo) if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
} }
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn); protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn);

View File

@ -101,7 +101,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break; break;
} }
float aimRotation = MathHelper.RadiansToDegrees((float)Math.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X)); float aimRotation = MathHelper.RadiansToDegrees((float)Math.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
while (Math.Abs(aimRotation - Rotation) > 180) while (Math.Abs(aimRotation - Rotation) > 180)
aimRotation += aimRotation < Rotation ? 360 : -360; aimRotation += aimRotation < Rotation ? 360 : -360;

View File

@ -14,6 +14,7 @@ using osu.Game.Configuration;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -151,6 +152,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
} }
} }
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
Body.AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : Body.AccentColour);
Body.BorderColour = skin.GetValue<SkinConfiguration, Color4>(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : Body.BorderColour);
Ball.AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : Ball.AccentColour);
}
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (userTriggered || Time.Current < slider.EndTime) if (userTriggered || Time.Current < slider.EndTime)

View File

@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using System.Linq; using System.Linq;
using osu.Framework.Caching;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -26,8 +27,11 @@ namespace osu.Game.Rulesets.Osu.Objects
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
public double Duration => EndTime - StartTime; public double Duration => EndTime - StartTime;
private Cached<Vector2> endPositionCache;
public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1);
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
public override Vector2 EndPosition => Position + this.CurvePositionAt(1);
public override int ComboIndex public override int ComboIndex
{ {
@ -56,7 +60,11 @@ namespace osu.Game.Rulesets.Osu.Objects
public SliderPath Path public SliderPath Path
{ {
get => PathBindable.Value; get => PathBindable.Value;
set => PathBindable.Value = value; set
{
PathBindable.Value = value;
endPositionCache.Invalidate();
}
} }
public double Distance => Path.Distance; public double Distance => Path.Distance;
@ -73,6 +81,8 @@ namespace osu.Game.Rulesets.Osu.Objects
if (TailCircle != null) if (TailCircle != null)
TailCircle.Position = EndPosition; TailCircle.Position = EndPosition;
endPositionCache.Invalidate();
} }
} }
@ -92,7 +102,17 @@ namespace osu.Game.Rulesets.Osu.Objects
public List<List<SampleInfo>> NodeSamples { get; set; } = new List<List<SampleInfo>>(); public List<List<SampleInfo>> NodeSamples { get; set; } = new List<List<SampleInfo>>();
public int RepeatCount { get; set; } private int repeatCount;
public int RepeatCount
{
get => repeatCount;
set
{
repeatCount = value;
endPositionCache.Invalidate();
}
}
/// <summary> /// <summary>
/// The length of one span of this <see cref="Slider"/>. /// The length of one span of this <see cref="Slider"/>.
@ -169,7 +189,11 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createTicks() private void createTicks()
{ {
var length = Path.Distance; // A very lenient maximum length of a slider for ticks to be generated.
// This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage.
const double max_length = 100000;
var length = Math.Min(max_length, Path.Distance);
var tickDistance = MathHelper.Clamp(TickDistance, 0, length); var tickDistance = MathHelper.Clamp(TickDistance, 0, length);
if (tickDistance == 0) return; if (tickDistance == 0) return;
@ -191,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.Objects
var distanceProgress = d / length; var distanceProgress = d / length;
var timeProgress = reversed ? 1 - distanceProgress : distanceProgress; var timeProgress = reversed ? 1 - distanceProgress : distanceProgress;
var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL) var firstSample = Samples.Find(s => s.Name == SampleInfo.HIT_NORMAL)
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
var sampleList = new List<SampleInfo>(); var sampleList = new List<SampleInfo>();

View File

@ -39,10 +39,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private int downCount; private int downCount;
private const float pressed_scale = 1.2f; private void updateExpandedState()
private const float released_scale = 1f; {
if (downCount > 0)
private float targetScale => downCount > 0 ? pressed_scale : released_scale; (ActiveCursor as OsuCursor)?.Expand();
else
(ActiveCursor as OsuCursor)?.Contract();
}
public bool OnPressed(OsuAction action) public bool OnPressed(OsuAction action)
{ {
@ -51,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton: case OsuAction.LeftButton:
case OsuAction.RightButton: case OsuAction.RightButton:
downCount++; downCount++;
ActiveCursor.ScaleTo(released_scale).ScaleTo(targetScale, 100, Easing.OutQuad); updateExpandedState();
break; break;
} }
@ -65,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton: case OsuAction.LeftButton:
case OsuAction.RightButton: case OsuAction.RightButton:
if (--downCount == 0) if (--downCount == 0)
ActiveCursor.ScaleTo(targetScale, 200, Easing.OutQuad); updateExpandedState();
break; break;
} }
@ -77,92 +80,106 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override void PopIn() protected override void PopIn()
{ {
fadeContainer.FadeTo(1, 300, Easing.OutQuint); fadeContainer.FadeTo(1, 300, Easing.OutQuint);
ActiveCursor.ScaleTo(targetScale, 400, Easing.OutQuint); ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
} }
protected override void PopOut() protected override void PopOut()
{ {
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint); fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(targetScale * 0.8f, 450, Easing.OutQuint); ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
} }
public class OsuCursor : Container public class OsuCursor : SkinReloadableDrawable
{ {
private Drawable cursorContainer; private bool cursorExpand;
private Bindable<double> cursorScale; private Bindable<double> cursorScale;
private Bindable<bool> autoCursorScale; private Bindable<bool> autoCursorScale;
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>(); private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private Container expandTarget;
private Drawable scaleTarget;
public OsuCursor() public OsuCursor()
{ {
Origin = Anchor.Centre; Origin = Anchor.Centre;
Size = new Vector2(42); Size = new Vector2(42);
} }
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
cursorExpand = skin.GetValue<SkinConfiguration, bool>(s => s.CursorExpand ?? true);
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config, IBindableBeatmap beatmap) private void load(OsuConfigManager config, IBindableBeatmap beatmap)
{ {
Child = cursorContainer = new SkinnableDrawable("cursor", _ => new CircularContainer InternalChild = expandTarget = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 6,
BorderColour = Color4.White,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Pink.Opacity(0.5f),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 3,
BorderColour = Color4.White.Opacity(0.5f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
},
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.1f),
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
},
},
}
}, restrictSize: false)
{
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both, Child = scaleTarget = new SkinnableDrawable("cursor", _ => new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 6,
BorderColour = Color4.White,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Pink.Opacity(0.5f),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 3,
BorderColour = Color4.White.Opacity(0.5f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
},
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.1f),
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
},
},
}
}, restrictSize: false)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
}
}; };
this.beatmap.BindTo(beatmap); this.beatmap.BindTo(beatmap);
@ -187,8 +204,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY); scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
} }
cursorContainer.Scale = new Vector2(scale); scaleTarget.Scale = new Vector2(scale);
} }
private const float pressed_scale = 1.2f;
private const float released_scale = 1f;
public void Expand()
{
if (!cursorExpand) return;
expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad);
}
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
} }
} }
} }

View File

@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
if (userTriggered) if (userTriggered)
{ {
var nextTick = ticks.FirstOrDefault(j => !j.IsHit); var nextTick = ticks.Find(j => !j.IsHit);
nextTick?.TriggerResult(HitResult.Great); nextTick?.TriggerResult(HitResult.Great);

View File

@ -149,7 +149,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var stream = Resource.OpenResource(filename)) using (var stream = Resource.OpenResource(filename))
using (var sr = new StreamReader(stream)) using (var sr = new StreamReader(stream))
{ {
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms)) using (var sw = new StreamWriter(ms))

View File

@ -40,7 +40,6 @@ namespace osu.Game.Tests.Visual
typeof(DrawableCarouselBeatmapSet), typeof(DrawableCarouselBeatmapSet),
}; };
private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>(); private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>();
private readonly HashSet<int> eagerSelectedIDs = new HashSet<int>(); private readonly HashSet<int> eagerSelectedIDs = new HashSet<int>();
@ -148,7 +147,7 @@ namespace osu.Game.Tests.Visual
private bool selectedBeatmapVisible() private bool selectedBeatmapVisible()
{ {
var currentlySelected = carousel.Items.FirstOrDefault(s => s.Item is CarouselBeatmap && s.Item.State == CarouselItemState.Selected); var currentlySelected = carousel.Items.Find(s => s.Item is CarouselBeatmap && s.Item.State == CarouselItemState.Selected);
if (currentlySelected == null) if (currentlySelected == null)
return true; return true;
return currentlySelected.Item.Visible; return currentlySelected.Item.Visible;

View File

@ -55,7 +55,6 @@ namespace osu.Game.Tests.Visual
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo }); AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
scores = new[] scores = new[]
{ {
new APIScoreInfo new APIScoreInfo

View File

@ -16,7 +16,6 @@ namespace osu.Game.Tests.Visual
public TestCaseBreadcrumbs() public TestCaseBreadcrumbs()
{ {
Add(breadcrumbs = new BreadcrumbControl<BreadcrumbTab> Add(breadcrumbs = new BreadcrumbControl<BreadcrumbTab>
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,

View File

@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual
linkColour = colours.Blue; linkColour = colours.Blue;
var chatManager = new ChannelManager(); var chatManager = new ChannelManager();
BindableCollection<Channel> availableChannels = (BindableCollection<Channel>)chatManager.AvailableChannels; BindableList<Channel> availableChannels = (BindableList<Channel>)chatManager.AvailableChannels;
availableChannels.Add(new Channel { Name = "#english"}); availableChannels.Add(new Channel { Name = "#english"});
availableChannels.Add(new Channel { Name = "#japanese" }); availableChannels.Add(new Channel { Name = "#japanese" });
Dependencies.Cache(chatManager); Dependencies.Cache(chatManager);

View File

@ -39,7 +39,6 @@ namespace osu.Game.Tests.Visual
}, },
}; };
AddStep("Add random", () => AddStep("Add random", () =>
{ {
Key key = (Key)((int)Key.A + RNG.Next(26)); Key key = (Key)((int)Key.A + RNG.Next(26));

View File

@ -73,8 +73,8 @@ namespace osu.Game.Tests.Visual
{ {
public event Action RoomsUpdated; public event Action RoomsUpdated;
public readonly BindableCollection<Room> Rooms = new BindableCollection<Room>(); public readonly BindableList<Room> Rooms = new BindableList<Room>();
IBindableCollection<Room> IRoomManager.Rooms => Rooms; IBindableList<Room> IRoomManager.Rooms => Rooms;
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) => Rooms.Add(room); public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) => Rooms.Add(room);

View File

@ -17,12 +17,13 @@ namespace osu.Game.Tests.Visual
{ {
public TestCaseMatchLeaderboard() public TestCaseMatchLeaderboard()
{ {
Add(new MatchLeaderboard(new Room { RoomID = { Value = 3 } }) Add(new MatchLeaderboard
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f), Size = new Vector2(550f, 450f),
Scope = MatchLeaderboardScope.Overall, Scope = MatchLeaderboardScope.Overall,
Room = new Room { RoomID = { Value = 3 } }
}); });
} }

View File

@ -138,7 +138,7 @@ namespace osu.Game.Tests.Visual
public event Action RoomsUpdated; public event Action RoomsUpdated;
public IBindableCollection<Room> Rooms { get; } = null; public IBindableList<Room> Rooms { get; } = null;
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{ {

View File

@ -49,7 +49,6 @@ namespace osu.Game.Tests.Visual
manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; }; manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; };
setState(Visibility.Visible); setState(Visibility.Visible);
AddStep(@"simple #1", sendHelloNotification); AddStep(@"simple #1", sendHelloNotification);
AddStep(@"simple #2", sendAmazingNotification); AddStep(@"simple #2", sendAmazingNotification);
@ -75,7 +74,6 @@ namespace osu.Game.Tests.Visual
checkProgressingCount(0); checkProgressingCount(0);
setState(Visibility.Visible); setState(Visibility.Visible);
//AddStep(@"barrage", () => sendBarrage()); //AddStep(@"barrage", () => sendBarrage());
@ -111,7 +109,7 @@ namespace osu.Game.Tests.Visual
if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3) if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3)
{ {
var p = progressingNotifications.FirstOrDefault(n => n.State == ProgressNotificationState.Queued); var p = progressingNotifications.Find(n => n.State == ProgressNotificationState.Queued);
if (p != null) if (p != null)
p.State = ProgressNotificationState.Active; p.State = ProgressNotificationState.Active;
} }

View File

@ -57,11 +57,19 @@ namespace osu.Game.Tests.Visual
private class TestSongSelect : PlaySongSelect private class TestSongSelect : PlaySongSelect
{ {
public Action StartRequested;
public new Bindable<RulesetInfo> Ruleset => base.Ruleset; public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
public WorkingBeatmap CurrentBeatmap => Beatmap.Value; public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap;
public new BeatmapCarousel Carousel => base.Carousel; public new BeatmapCarousel Carousel => base.Carousel;
protected override bool OnStart()
{
StartRequested?.Invoke();
return base.OnStart();
}
} }
private TestSongSelect songSelect; private TestSongSelect songSelect;
@ -182,6 +190,27 @@ namespace osu.Game.Tests.Visual
void onRulesetChange(RulesetInfo ruleset) => rulesetChangeIndex = actionIndex--; void onRulesetChange(RulesetInfo ruleset) => rulesetChangeIndex = actionIndex--;
} }
[Test]
public void TestStartAfterUnMatchingFilterDoesNotStart()
{
addManyTestMaps();
AddUntilStep(() => songSelect.Carousel.SelectedBeatmap != null, "has selection");
bool startRequested = false;
AddStep("set filter and finalize", () =>
{
songSelect.StartRequested = () => startRequested = true;
songSelect.Carousel.Filter(new FilterCriteria { SearchText = "somestringthatshouldn'tbematchable" });
songSelect.FinaliseSelection();
songSelect.StartRequested = null;
});
AddAssert("start not requested", () => !startRequested);
}
private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray()))); private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())));
private static int importId; private static int importId;

View File

@ -57,5 +57,4 @@ namespace osu.Game.Tests.Visual
} }
} }
} }
} }

View File

@ -5,7 +5,6 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
@ -23,7 +22,7 @@ namespace osu.Game.Tests.Visual
// Reset the mods // Reset the mods
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Where(m => !(m is ModAutoplay)); Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Where(m => !(m is ModAutoplay));
return new ReplayPlayer(new Score { Replay = dummyRulesetContainer.Replay }); return new ReplayPlayer(dummyRulesetContainer.ReplayScore);
} }
} }
} }

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
@ -9,6 +10,7 @@ using Newtonsoft.Json;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.IO.Serialization; using osu.Game.IO.Serialization;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Scoring;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
@ -112,6 +114,11 @@ namespace osu.Game.Beatmaps
[JsonProperty("difficulty_rating")] [JsonProperty("difficulty_rating")]
public double StarDifficulty { get; set; } public double StarDifficulty { get; set; }
/// <summary>
/// Currently only populated for beatmap deletion. Use <see cref="ScoreManager"/> to query scores.
/// </summary>
public List<ScoreInfo> Scores { get; set; }
public override string ToString() => $"{Metadata} [{Version}]"; public override string ToString() => $"{Metadata} [{Version}]";
public bool Equals(BeatmapInfo other) public bool Equals(BeatmapInfo other)

View File

@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps
public string Hash { get; set; } public string Hash { get; set; }
public string StoryboardFile => Files?.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename; public string StoryboardFile => Files?.Find(f => f.Filename.EndsWith(".osb"))?.Filename;
public List<BeatmapSetFileInfo> Files { get; set; } public List<BeatmapSetFileInfo> Files { get; set; }

View File

@ -64,7 +64,8 @@ namespace osu.Game.Beatmaps
base.AddIncludesForDeletion(query) base.AddIncludesForDeletion(query)
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
.Include(s => s.Metadata); .Include(s => s.Metadata)
.Include(s => s.Beatmaps).ThenInclude(b => b.Scores);
protected override IQueryable<BeatmapSetInfo> AddIncludesForConsumption(IQueryable<BeatmapSetInfo> query) => protected override IQueryable<BeatmapSetInfo> AddIncludesForConsumption(IQueryable<BeatmapSetInfo> query) =>
base.AddIncludesForConsumption(query) base.AddIncludesForConsumption(query)

View File

@ -151,7 +151,7 @@ namespace osu.Game.Beatmaps
public bool WaveformLoaded => waveform.IsResultAvailable; public bool WaveformLoaded => waveform.IsResultAvailable;
public Waveform Waveform => waveform.Value; public Waveform Waveform => waveform.Value;
protected virtual Waveform GetWaveform() => new Waveform(); protected virtual Waveform GetWaveform() => new Waveform(null);
private readonly RecyclableLazy<Waveform> waveform; private readonly RecyclableLazy<Waveform> waveform;
public bool StoryboardLoaded => storyboard.IsResultAvailable; public bool StoryboardLoaded => storyboard.IsResultAvailable;

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -43,7 +42,7 @@ namespace osu.Game.Configuration
{ {
base.AddBindable(lookup, bindable); base.AddBindable(lookup, bindable);
var setting = databasedSettings.FirstOrDefault(s => (int)s.Key == (int)(object)lookup); var setting = databasedSettings.Find(s => (int)s.Key == (int)(object)lookup);
if (setting != null) if (setting != null)
{ {
bindable.Parse(setting.Value); bindable.Parse(setting.Value);

View File

@ -3,6 +3,7 @@
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking; using osu.Framework.Configuration.Tracking;
using osu.Framework.Extensions;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -96,15 +97,27 @@ namespace osu.Game.Configuration
Set(OsuSetting.ScreenshotCaptureMenuCursor, false); Set(OsuSetting.ScreenshotCaptureMenuCursor, false);
Set(OsuSetting.SongSelectRightMouseScroll, false); Set(OsuSetting.SongSelectRightMouseScroll, false);
Set(OsuSetting.Scaling, ScalingMode.Off);
Set(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f);
Set(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f);
Set(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f);
Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f);
Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
} }
public OsuConfigManager(Storage storage) : base(storage) public OsuConfigManager(Storage storage)
: base(storage)
{ {
} }
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
{ {
new TrackedSetting<bool>(OsuSetting.MouseDisableButtons, v => new SettingDescription(!v, "gameplay mouse buttons", v ? "disabled" : "enabled")) new TrackedSetting<bool>(OsuSetting.MouseDisableButtons, v => new SettingDescription(!v, "gameplay mouse buttons", v ? "disabled" : "enabled")),
new TrackedSetting<ScalingMode>(OsuSetting.Scaling, m => new SettingDescription(m, "scaling", m.GetDescription())),
}; };
} }
@ -151,6 +164,12 @@ namespace osu.Game.Configuration
BeatmapHitsounds, BeatmapHitsounds,
IncreaseFirstObjectVisibility, IncreaseFirstObjectVisibility,
ScoreDisplayMode, ScoreDisplayMode,
ExternalLinkWarning ExternalLinkWarning,
Scaling,
ScalingPositionX,
ScalingPositionY,
ScalingSizeX,
ScalingSizeY,
UIScale
} }
} }

View File

@ -0,0 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Configuration
{
public enum ScalingMode
{
Off,
Everything,
[Description("Excluding overlays")]
ExcludeOverlays,
Gameplay,
}
}

View File

@ -79,6 +79,7 @@ namespace osu.Game.Graphics.Containers
{ {
AddInternal(new DrawableLinkCompiler(drawables.OfType<SpriteText>().ToList()) AddInternal(new DrawableLinkCompiler(drawables.OfType<SpriteText>().ToList())
{ {
RelativeSizeAxes = Axes.Both,
TooltipText = tooltipText ?? (url != text ? url : string.Empty), TooltipText = tooltipText ?? (url != text ? url : string.Empty),
Action = action ?? (() => Action = action ?? (() =>
{ {
@ -122,5 +123,10 @@ namespace osu.Game.Graphics.Containers
}), }),
}); });
} }
// We want the compilers to always be visible no matter where they are, so RelativeSizeAxes is used.
// However due to https://github.com/ppy/osu-framework/issues/2073, it's possible for the compilers to be relative size in the flow's auto-size axes - an unsupported operation.
// Since the compilers don't display any content and don't affect the layout, it's simplest to exclude them from the flow.
public override IEnumerable<Drawable> FlowingChildren => base.FlowingChildren.Where(c => !(c is DrawableLinkCompiler));
} }
} }

View File

@ -0,0 +1,147 @@
// Copyright (c) 2007-2018 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.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
using osu.Game.Graphics.Backgrounds;
using osuTK;
namespace osu.Game.Graphics.Containers
{
/// <summary>
/// Handles user-defined scaling, allowing application at multiple levels defined by <see cref="ScalingMode"/>.
/// </summary>
public class ScalingContainer : Container
{
private Bindable<float> sizeX;
private Bindable<float> sizeY;
private Bindable<float> posX;
private Bindable<float> posY;
private readonly ScalingMode? targetMode;
private Bindable<ScalingMode> scalingMode;
private readonly Container content;
protected override Container<Drawable> Content => content;
private readonly Container sizableContainer;
private Drawable backgroundLayer;
/// <summary>
/// Create a new instance.
/// </summary>
/// <param name="targetMode">The mode which this container should be handling. Handles all modes if null.</param>
public ScalingContainer(ScalingMode? targetMode = null)
{
this.targetMode = targetMode;
RelativeSizeAxes = Axes.Both;
InternalChild = sizableContainer = new Container
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
CornerRadius = 10,
Child = content = new ScalingDrawSizePreservingFillContainer(targetMode != ScalingMode.Gameplay)
};
}
private class ScalingDrawSizePreservingFillContainer : DrawSizePreservingFillContainer
{
private readonly bool applyUIScale;
private Bindable<float> uiScale;
public ScalingDrawSizePreservingFillContainer(bool applyUIScale)
{
this.applyUIScale = applyUIScale;
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager osuConfig)
{
if (applyUIScale)
{
uiScale = osuConfig.GetBindable<float>(OsuSetting.UIScale);
uiScale.BindValueChanged(scaleChanged, true);
}
}
private void scaleChanged(float value)
{
this.ScaleTo(new Vector2(value), 500, Easing.Out);
this.ResizeTo(new Vector2(1 / value), 500, Easing.Out);
}
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
scalingMode = config.GetBindable<ScalingMode>(OsuSetting.Scaling);
scalingMode.ValueChanged += _ => updateSize();
sizeX = config.GetBindable<float>(OsuSetting.ScalingSizeX);
sizeX.ValueChanged += _ => updateSize();
sizeY = config.GetBindable<float>(OsuSetting.ScalingSizeY);
sizeY.ValueChanged += _ => updateSize();
posX = config.GetBindable<float>(OsuSetting.ScalingPositionX);
posX.ValueChanged += _ => updateSize();
posY = config.GetBindable<float>(OsuSetting.ScalingPositionY);
posY.ValueChanged += _ => updateSize();
}
protected override void LoadComplete()
{
base.LoadComplete();
updateSize();
sizableContainer.FinishTransforms();
}
private bool requiresBackgroundVisible => (scalingMode == ScalingMode.Everything || scalingMode == ScalingMode.ExcludeOverlays) && (sizeX.Value != 1 || sizeY.Value != 1);
private void updateSize()
{
if (targetMode == ScalingMode.Everything)
{
// the top level scaling container manages the background to be displayed while scaling.
if (requiresBackgroundVisible)
{
if (backgroundLayer == null)
LoadComponentAsync(backgroundLayer = new Background("Menu/menu-background-1")
{
Colour = OsuColour.Gray(0.1f),
Alpha = 0,
Depth = float.MaxValue
}, d =>
{
AddInternal(d);
d.FadeTo(requiresBackgroundVisible ? 1 : 0, 4000, Easing.OutQuint);
});
else
backgroundLayer.FadeIn(500);
}
else
backgroundLayer?.FadeOut(500);
}
bool scaling = targetMode == null || scalingMode.Value == targetMode;
var targetSize = scaling ? new Vector2(sizeX, sizeY) : Vector2.One;
var targetPosition = scaling ? new Vector2(posX, posY) * (Vector2.One - targetSize) : Vector2.Zero;
bool requiresMasking = scaling && targetSize != Vector2.One;
if (requiresMasking)
sizableContainer.Masking = true;
sizableContainer.MoveTo(targetPosition, 500, Easing.OutQuart);
sizableContainer.ResizeTo(targetSize, 500, Easing.OutQuart).OnComplete(_ => { sizableContainer.Masking = requiresMasking; });
}
}
}

View File

@ -8,7 +8,6 @@ using osuTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
@ -33,38 +32,7 @@ namespace osu.Game.Graphics.UserInterface
private readonly Box leftBox; private readonly Box leftBox;
private readonly Box rightBox; private readonly Box rightBox;
public virtual string TooltipText public virtual string TooltipText { get; private set; }
{
get
{
var bindableDouble = CurrentNumber as BindableNumber<double>;
var bindableFloat = CurrentNumber as BindableNumber<float>;
var floatValue = bindableDouble?.Value ?? bindableFloat?.Value;
var floatPrecision = bindableDouble?.Precision ?? bindableFloat?.Precision;
if (floatValue != null)
{
var floatMinValue = bindableDouble?.MinValue ?? bindableFloat.MinValue;
var floatMaxValue = bindableDouble?.MaxValue ?? bindableFloat.MaxValue;
if (floatMaxValue == 1 && (floatMinValue == 0 || floatMinValue == -1))
return floatValue.Value.ToString("P0");
var decimalPrecision = normalise((decimal)floatPrecision, max_decimal_digits);
// Find the number of significant digits (we could have less than 5 after normalize())
var significantDigits = findPrecision(decimalPrecision);
return floatValue.Value.ToString($"N{significantDigits}");
}
var bindableInt = CurrentNumber as BindableNumber<int>;
if (bindableInt != null)
return bindableInt.Value.ToString("N0");
return Current.Value.ToString(CultureInfo.InvariantCulture);
}
}
private Color4 accentColour; private Color4 accentColour;
public Color4 AccentColour public Color4 AccentColour
@ -136,21 +104,34 @@ namespace osu.Game.Graphics.UserInterface
base.OnHoverLost(e); base.OnHoverLost(e);
} }
protected override void OnUserChange() protected override bool OnMouseDown(MouseDownEvent e)
{ {
base.OnUserChange(); Nub.Current.Value = true;
playSample(); return base.OnMouseDown(e);
} }
private void playSample() protected override bool OnMouseUp(MouseUpEvent e)
{
Nub.Current.Value = false;
return base.OnMouseUp(e);
}
protected override void OnUserChange(T value)
{
base.OnUserChange(value);
playSample(value);
updateTooltipText(value);
}
private void playSample(T value)
{ {
if (Clock == null || Clock.CurrentTime - lastSampleTime <= 50) if (Clock == null || Clock.CurrentTime - lastSampleTime <= 50)
return; return;
if (Current.Value.Equals(lastSampleValue)) if (value.Equals(lastSampleValue))
return; return;
lastSampleValue = Current.Value; lastSampleValue = value;
lastSampleTime = Clock.CurrentTime; lastSampleTime = Clock.CurrentTime;
sample.Frequency.Value = 1 + NormalizedValue * 0.2f; sample.Frequency.Value = 1 + NormalizedValue * 0.2f;
@ -163,16 +144,28 @@ namespace osu.Game.Graphics.UserInterface
sample.Play(); sample.Play();
} }
protected override bool OnMouseDown(MouseDownEvent e) private void updateTooltipText(T value)
{ {
Nub.Current.Value = true; if (CurrentNumber.IsInteger)
return base.OnMouseDown(e); TooltipText = ((int)Convert.ChangeType(value, typeof(int))).ToString("N0");
} else
{
double floatValue = (double)Convert.ChangeType(value, typeof(double));
double floatMinValue = (double)Convert.ChangeType(CurrentNumber.MinValue, typeof(double));
double floatMaxValue = (double)Convert.ChangeType(CurrentNumber.MaxValue, typeof(double));
protected override bool OnMouseUp(MouseUpEvent e) if (floatMaxValue == 1 && floatMinValue >= -1)
{ TooltipText = floatValue.ToString("P0");
Nub.Current.Value = false; else
return base.OnMouseUp(e); {
var decimalPrecision = normalise((decimal)Convert.ChangeType(CurrentNumber.Precision, typeof(decimal)), max_decimal_digits);
// Find the number of significant digits (we could have less than 5 after normalize())
var significantDigits = findPrecision(decimalPrecision);
TooltipText = floatValue.ToString($"N{significantDigits}");
}
}
} }
protected override void UpdateAfterChildren() protected override void UpdateAfterChildren()

View File

@ -62,6 +62,6 @@ namespace osu.Game.Graphics.UserInterface
fill.Width = value * UsableWidth; fill.Width = value * UsableWidth;
} }
protected override void OnUserChange() => OnSeek?.Invoke(Current); protected override void OnUserChange(double value) => OnSeek?.Invoke(value);
} }
} }

View File

@ -14,7 +14,7 @@ namespace osu.Game.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024"); .HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{ {
@ -215,6 +215,25 @@ namespace osu.Game.Migrations
b.ToTable("Settings"); b.ToTable("Settings");
}); });
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{ {
b.Property<int>("ID") b.Property<int>("ID")
@ -239,25 +258,6 @@ namespace osu.Game.Migrations
b.ToTable("KeyBinding"); b.ToTable("KeyBinding");
}); });
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{ {
b.Property<int?>("ID") b.Property<int?>("ID")
@ -454,7 +454,7 @@ namespace osu.Game.Migrations
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{ {
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany() .WithMany("Scores")
.HasForeignKey("BeatmapInfoID") .HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);

View File

@ -101,6 +101,9 @@ namespace osu.Game.Online.API
//todo: replace this with a ping request. //todo: replace this with a ping request.
log.Add(@"In a failing state, waiting a bit before we try again..."); log.Add(@"In a failing state, waiting a bit before we try again...");
Thread.Sleep(5000); Thread.Sleep(5000);
if (!IsLoggedIn) goto case APIState.Connecting;
if (queue.Count == 0) if (queue.Count == 0)
{ {
log.Add(@"Queueing a ping request"); log.Add(@"Queueing a ping request");

View File

@ -41,7 +41,6 @@ namespace osu.Game.Online.Chat
/// </summary> /// </summary>
private readonly List<LocalEchoMessage> pendingMessages = new List<LocalEchoMessage>(); private readonly List<LocalEchoMessage> pendingMessages = new List<LocalEchoMessage>();
/// <summary> /// <summary>
/// An event that fires when new messages arrived. /// An event that fires when new messages arrived.
/// </summary> /// </summary>

View File

@ -29,8 +29,8 @@ namespace osu.Game.Online.Chat
@"#lobby" @"#lobby"
}; };
private readonly BindableCollection<Channel> availableChannels = new BindableCollection<Channel>(); private readonly BindableList<Channel> availableChannels = new BindableList<Channel>();
private readonly BindableCollection<Channel> joinedChannels = new BindableCollection<Channel>(); private readonly BindableList<Channel> joinedChannels = new BindableList<Channel>();
/// <summary> /// <summary>
/// The currently opened channel /// The currently opened channel
@ -40,12 +40,12 @@ namespace osu.Game.Online.Chat
/// <summary> /// <summary>
/// The Channels the player has joined /// The Channels the player has joined
/// </summary> /// </summary>
public IBindableCollection<Channel> JoinedChannels => joinedChannels; public IBindableList<Channel> JoinedChannels => joinedChannels;
/// <summary> /// <summary>
/// The channels available for the player to join /// The channels available for the player to join
/// </summary> /// </summary>
public IBindableCollection<Channel> AvailableChannels => availableChannels; public IBindableList<Channel> AvailableChannels => availableChannels;
private IAPIProvider api; private IAPIProvider api;

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -30,6 +31,7 @@ namespace osu.Game.Online.Leaderboards
private readonly LoadingAnimation loading; private readonly LoadingAnimation loading;
private ScheduledDelegate showScoresDelegate; private ScheduledDelegate showScoresDelegate;
private CancellationTokenSource showScoresCancellationSource;
private bool scoresLoadedOnce; private bool scoresLoadedOnce;
@ -49,6 +51,10 @@ namespace osu.Game.Online.Leaderboards
loading.Hide(); loading.Hide();
// schedule because we may not be loaded yet (LoadComponentAsync complains).
showScoresDelegate?.Cancel();
showScoresCancellationSource?.Cancel();
if (scores == null || !scores.Any()) if (scores == null || !scores.Any())
return; return;
@ -58,8 +64,6 @@ namespace osu.Game.Online.Leaderboards
scrollFlow = CreateScoreFlow(); scrollFlow = CreateScoreFlow();
scrollFlow.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1)); scrollFlow.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1));
// schedule because we may not be loaded yet (LoadComponentAsync complains).
showScoresDelegate?.Cancel();
if (!IsLoaded) if (!IsLoaded)
showScoresDelegate = Schedule(showScores); showScoresDelegate = Schedule(showScores);
else else
@ -77,7 +81,7 @@ namespace osu.Game.Online.Leaderboards
} }
scrollContainer.ScrollTo(0f, false); scrollContainer.ScrollTo(0f, false);
}); }, (showScoresCancellationSource = new CancellationTokenSource()).Token);
} }
} }

View File

@ -37,10 +37,10 @@ namespace osu.Game.Online.Multiplayer
public RulesetInfo Ruleset { get; set; } public RulesetInfo Ruleset { get; set; }
[JsonIgnore] [JsonIgnore]
public readonly BindableCollection<Mod> AllowedMods = new BindableCollection<Mod>(); public readonly BindableList<Mod> AllowedMods = new BindableList<Mod>();
[JsonIgnore] [JsonIgnore]
public readonly BindableCollection<Mod> RequiredMods = new BindableCollection<Mod>(); public readonly BindableList<Mod> RequiredMods = new BindableList<Mod>();
[JsonProperty("beatmap")] [JsonProperty("beatmap")]
private APIBeatmap apiBeatmap { get; set; } private APIBeatmap apiBeatmap { get; set; }

View File

@ -24,7 +24,7 @@ namespace osu.Game.Online.Multiplayer
public Bindable<User> Host { get; private set; } = new Bindable<User>(); public Bindable<User> Host { get; private set; } = new Bindable<User>();
[JsonProperty("playlist")] [JsonProperty("playlist")]
public BindableCollection<PlaylistItem> Playlist { get; set; } = new BindableCollection<PlaylistItem>(); public BindableList<PlaylistItem> Playlist { get; set; } = new BindableList<PlaylistItem>();
[JsonProperty("channel_id")] [JsonProperty("channel_id")]
public Bindable<int> ChannelId { get; private set; } = new Bindable<int>(); public Bindable<int> ChannelId { get; private set; } = new Bindable<int>();

View File

@ -26,6 +26,7 @@ using osu.Framework.Platform;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Input; using osu.Game.Input;
using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -187,6 +188,7 @@ namespace osu.Game
} }
private ExternalLinkOpener externalLinkOpener; private ExternalLinkOpener externalLinkOpener;
public void OpenUrlExternally(string url) public void OpenUrlExternally(string url)
{ {
if (url.StartsWith("/")) if (url.StartsWith("/"))
@ -222,7 +224,7 @@ namespace osu.Game
var databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID); var databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
// Use first beatmap available for current ruleset, else switch ruleset. // Use first beatmap available for current ruleset, else switch ruleset.
var first = databasedSet.Beatmaps.FirstOrDefault(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First(); var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
ruleset.Value = first.Ruleset; ruleset.Value = first.Ruleset;
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first); Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
@ -353,7 +355,14 @@ namespace osu.Game
ActionRequested = action => volume.Adjust(action), ActionRequested = action => volume.Adjust(action),
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise), ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
}, },
mainContent = new Container { RelativeSizeAxes = Axes.Both }, screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays)
{
RelativeSizeAxes = Axes.Both,
},
mainContent = new Container
{
RelativeSizeAxes = Axes.Both,
},
overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue },
idleTracker = new IdleTracker(6000) idleTracker = new IdleTracker(6000)
}); });
@ -362,7 +371,7 @@ namespace osu.Game
{ {
screenStack.ModePushed += screenAdded; screenStack.ModePushed += screenAdded;
screenStack.Exited += screenRemoved; screenStack.Exited += screenRemoved;
mainContent.Add(screenStack); screenContainer.Add(screenStack);
}); });
loadComponentSingleFile(Toolbar = new Toolbar loadComponentSingleFile(Toolbar = new Toolbar
@ -497,7 +506,7 @@ namespace osu.Game
if (notifications.State == Visibility.Visible) if (notifications.State == Visibility.Visible)
offset -= ToolbarButton.WIDTH / 2; offset -= ToolbarButton.WIDTH / 2;
screenStack.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint); screenContainer.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint);
} }
settings.StateChanged += _ => updateScreenOffset(); settings.StateChanged += _ => updateScreenOffset();
@ -555,7 +564,7 @@ namespace osu.Game
focused.StateChanged += s => focused.StateChanged += s =>
{ {
visibleOverlayCount += s == Visibility.Visible ? 1 : -1; visibleOverlayCount += s == Visibility.Visible ? 1 : -1;
screenStack.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint); screenContainer.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint);
}; };
} }
@ -646,6 +655,7 @@ namespace osu.Game
private OsuScreen currentScreen; private OsuScreen currentScreen;
private FrameworkConfigManager frameworkConfig; private FrameworkConfigManager frameworkConfig;
private ScalingContainer screenContainer;
protected override bool OnExiting() protected override bool OnExiting()
{ {
@ -685,7 +695,7 @@ namespace osu.Game
ruleset.Disabled = applyBeatmapRulesetRestrictions; ruleset.Disabled = applyBeatmapRulesetRestrictions;
Beatmap.Disabled = applyBeatmapRulesetRestrictions; Beatmap.Disabled = applyBeatmapRulesetRestrictions;
mainContent.Padding = new MarginPadding { Top = ToolbarOffset }; screenContainer.Padding = new MarginPadding { Top = ToolbarOffset };
MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
} }

View File

@ -24,6 +24,7 @@ using osu.Framework.Input;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics.Containers;
using osu.Game.Input; using osu.Game.Input;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.IO; using osu.Game.IO;
@ -189,7 +190,7 @@ namespace osu.Game
Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both } Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }
}; };
base.Content.Add(new DrawSizePreservingFillContainer { Child = MenuCursorContainer }); base.Content.Add(new ScalingContainer(ScalingMode.Everything) { Child = MenuCursorContainer });
KeyBindingStore.Register(globalBinding); KeyBindingStore.Register(globalBinding);
dependencies.Cache(globalBinding); dependencies.Cache(globalBinding);
@ -247,7 +248,8 @@ namespace osu.Game
var extension = Path.GetExtension(paths.First())?.ToLowerInvariant(); var extension = Path.GetExtension(paths.First())?.ToLowerInvariant();
foreach (var importer in fileImporters) foreach (var importer in fileImporters)
if (importer.HandledExtensions.Contains(extension)) importer.Import(paths); if (importer.HandledExtensions.Contains(extension))
importer.Import(paths);
} }
public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray(); public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray();

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osuTK; using osuTK;
@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Chat.Selection
public readonly FillFlowContainer<ChannelListItem> ChannelFlow; public readonly FillFlowContainer<ChannelListItem> ChannelFlow;
public IEnumerable<IFilterable> FilterableChildren => ChannelFlow.Children; public IEnumerable<IFilterable> FilterableChildren => ChannelFlow.Children;
public IEnumerable<string> FilterTerms => new[] { Header }; public IEnumerable<string> FilterTerms => Array.Empty<string>();
public bool MatchingFilter public bool MatchingFilter
{ {
set set

View File

@ -77,7 +77,6 @@ namespace osu.Game.Overlays.Chat.Tabs
CloseButton.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); CloseButton.FadeIn(TRANSITION_LENGTH, Easing.OutQuint);
} }
protected override void FadeInactive() protected override void FadeInactive()
{ {
base.FadeInactive(); base.FadeInactive();

View File

@ -18,7 +18,6 @@ namespace osu.Game.Overlays.KeyBinding
Add(new InGameKeyBindingsSubsection(manager)); Add(new InGameKeyBindingsSubsection(manager));
} }
private class DefaultBindingsSubsection : KeyBindingsSubsection private class DefaultBindingsSubsection : KeyBindingsSubsection
{ {
protected override string Header => string.Empty; protected override string Header => string.Empty;

View File

@ -176,7 +176,6 @@ namespace osu.Game.Overlays.Mods
section.DeselectTypes(modTypes, immediate); section.DeselectTypes(modTypes, immediate);
} }
private SampleChannel sampleOn, sampleOff; private SampleChannel sampleOn, sampleOff;
private void modButtonPressed(Mod selectedMod) private void modButtonPressed(Mod selectedMod)

View File

@ -396,11 +396,11 @@ namespace osu.Game.Overlays.Profile
infoTextLeft.NewLine(); infoTextLeft.NewLine();
infoTextLeft.AddText("Last seen ", lightText); infoTextLeft.AddText("Last seen ", lightText);
infoTextLeft.AddText(new DrawableDate(user.LastVisit.Value), boldItalic); infoTextLeft.AddText(new DrawableDate(user.LastVisit.Value), boldItalic);
infoTextLeft.NewParagraph();
} }
if (user.PlayStyle?.Length > 0) if (user.PlayStyle?.Length > 0)
{ {
infoTextLeft.NewParagraph();
infoTextLeft.AddText("Plays with ", lightText); infoTextLeft.AddText("Plays with ", lightText);
infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic); infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic);
} }

View File

@ -49,7 +49,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
Api.Queue(request); Api.Queue(request);
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);

View File

@ -6,9 +6,14 @@ using System.Drawing;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Configuration;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
namespace osu.Game.Overlays.Settings.Sections.Graphics namespace osu.Game.Overlays.Settings.Sections.Graphics
{ {
@ -16,24 +21,33 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{ {
protected override string Header => "Layout"; protected override string Header => "Layout";
private FillFlowContainer letterboxSettings; private FillFlowContainer<SettingsSlider<float>> scalingSettings;
private Bindable<bool> letterboxing; private Bindable<ScalingMode> scalingMode;
private Bindable<Size> sizeFullscreen; private Bindable<Size> sizeFullscreen;
private OsuGameBase game; private OsuGameBase game;
private SettingsDropdown<Size> resolutionDropdown; private SettingsDropdown<Size> resolutionDropdown;
private SettingsEnumDropdown<WindowMode> windowModeDropdown; private SettingsEnumDropdown<WindowMode> windowModeDropdown;
private Bindable<float> scalingPositionX;
private Bindable<float> scalingPositionY;
private Bindable<float> scalingSizeX;
private Bindable<float> scalingSizeY;
private const int transition_duration = 400; private const int transition_duration = 400;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(FrameworkConfigManager config, OsuGameBase game) private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, OsuGameBase game)
{ {
this.game = game; this.game = game;
letterboxing = config.GetBindable<bool>(FrameworkSetting.Letterboxing); scalingMode = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling);
sizeFullscreen = config.GetBindable<Size>(FrameworkSetting.SizeFullscreen); sizeFullscreen = config.GetBindable<Size>(FrameworkSetting.SizeFullscreen);
scalingSizeX = osuConfig.GetBindable<float>(OsuSetting.ScalingSizeX);
scalingSizeY = osuConfig.GetBindable<float>(OsuSetting.ScalingSizeY);
scalingPositionX = osuConfig.GetBindable<float>(OsuSetting.ScalingPositionX);
scalingPositionY = osuConfig.GetBindable<float>(OsuSetting.ScalingPositionY);
Container resolutionSettingsContainer; Container resolutionSettingsContainer;
@ -49,12 +63,19 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y AutoSizeAxes = Axes.Y
}, },
new SettingsCheckbox new SettingsSlider<float, UIScaleSlider>
{ {
LabelText = "Letterboxing", LabelText = "UI Scaling",
Bindable = letterboxing, TransferValueOnCommit = true,
Bindable = osuConfig.GetBindable<float>(OsuSetting.UIScale),
KeyboardStep = 0.01f
}, },
letterboxSettings = new FillFlowContainer new SettingsEnumDropdown<ScalingMode>
{
LabelText = "Screen Scaling",
Bindable = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling),
},
scalingSettings = new FillFlowContainer<SettingsSlider<float>>
{ {
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
@ -62,25 +83,38 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
AutoSizeDuration = transition_duration, AutoSizeDuration = transition_duration,
AutoSizeEasing = Easing.OutQuint, AutoSizeEasing = Easing.OutQuint,
Masking = true, Masking = true,
Children = new []
Children = new Drawable[]
{ {
new SettingsSlider<double> new SettingsSlider<float>
{ {
LabelText = "Horizontal position", LabelText = "Horizontal position",
Bindable = config.GetBindable<double>(FrameworkSetting.LetterboxPositionX), Bindable = scalingPositionX,
KeyboardStep = 0.01f KeyboardStep = 0.01f
}, },
new SettingsSlider<double> new SettingsSlider<float>
{ {
LabelText = "Vertical position", LabelText = "Vertical position",
Bindable = config.GetBindable<double>(FrameworkSetting.LetterboxPositionY), Bindable = scalingPositionY,
KeyboardStep = 0.01f
},
new SettingsSlider<float>
{
LabelText = "Horizontal scale",
Bindable = scalingSizeX,
KeyboardStep = 0.01f
},
new SettingsSlider<float>
{
LabelText = "Vertical scale",
Bindable = scalingSizeY,
KeyboardStep = 0.01f KeyboardStep = 0.01f
}, },
} }
}, },
}; };
scalingSettings.ForEach(s => bindPreviewEvent(s.Bindable));
var resolutions = getResolutions(); var resolutions = getResolutions();
if (resolutions.Count > 1) if (resolutions.Count > 1)
@ -105,16 +139,46 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
}, true); }, true);
} }
letterboxing.BindValueChanged(isVisible => scalingMode.BindValueChanged(mode =>
{ {
letterboxSettings.ClearTransforms(); scalingSettings.ClearTransforms();
letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None; scalingSettings.AutoSizeAxes = mode != ScalingMode.Off ? Axes.Y : Axes.None;
if (!isVisible) if (mode == ScalingMode.Off)
letterboxSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint); scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
scalingSettings.ForEach(s => s.TransferValueOnCommit = mode == ScalingMode.Everything);
}, true); }, true);
} }
/// <summary>
/// Create a delayed bindable which only updates when a condition is met.
/// </summary>
/// <param name="bindable">The config bindable.</param>
/// <returns>A bindable which will propagate updates with a delay.</returns>
private void bindPreviewEvent(Bindable<float> bindable)
{
bindable.ValueChanged += v =>
{
switch (scalingMode.Value)
{
case ScalingMode.Gameplay:
showPreview();
break;
}
};
}
private Drawable preview;
private void showPreview()
{
if (preview?.IsAlive != true)
game.Add(preview = new ScalingPreview());
preview.FadeOutFromOne(1500);
preview.Expire();
}
private IReadOnlyList<Size> getResolutions() private IReadOnlyList<Size> getResolutions()
{ {
var resolutions = new List<Size> { new Size(9999, 9999) }; var resolutions = new List<Size> { new Size(9999, 9999) };
@ -132,6 +196,24 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
return resolutions; return resolutions;
} }
private class ScalingPreview : ScalingContainer
{
public ScalingPreview()
{
Child = new Box
{
Colour = Color4.White,
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f,
};
}
}
private class UIScaleSlider : OsuSliderBar<float>
{
public override string TooltipText => base.TooltipText + "x";
}
private class ResolutionSettingsDropdown : SettingsDropdown<Size> private class ResolutionSettingsDropdown : SettingsDropdown<Size>
{ {
protected override OsuDropdown<Size> CreateDropdown() => new ResolutionDropdownControl { Items = Items }; protected override OsuDropdown<Size> CreateDropdown() => new ResolutionDropdownControl { Items = Items };

View File

@ -1,20 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
namespace osu.Game.Overlays.Settings
{
public class SettingsLabel : SettingsItem<string>
{
protected override Drawable CreateControl() => null;
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
Colour = colour.Gray6;
}
}
}

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -23,14 +22,16 @@ namespace osu.Game.Overlays.Settings
RelativeSizeAxes = Axes.X RelativeSizeAxes = Axes.X
}; };
public float KeyboardStep; public bool TransferValueOnCommit
[BackgroundDependencyLoader]
private void load()
{ {
var slider = Control as U; get => ((U)Control).TransferValueOnCommit;
if (slider != null) set => ((U)Control).TransferValueOnCommit = value;
slider.KeyboardStep = KeyboardStep; }
public float KeyboardStep
{
get => ((U)Control).KeyboardStep;
set => ((U)Control).KeyboardStep = value;
} }
} }
} }

View File

@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mods
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer) => rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay); public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer) => rulesetContainer.SetReplayScore(CreateReplayScore(rulesetContainer.Beatmap));
} }
public abstract class ModAutoplay : Mod, IApplicableFailOverride public abstract class ModAutoplay : Mod, IApplicableFailOverride

View File

@ -124,7 +124,6 @@ namespace osu.Game.Rulesets.Objects.Legacy
// osu-stable treated the first span of the slider as a repeat, but no repeats are happening // osu-stable treated the first span of the slider as a repeat, but no repeats are happening
repeatCount = Math.Max(0, repeatCount - 1); repeatCount = Math.Max(0, repeatCount - 1);
if (split.Length > 7) if (split.Length > 7)
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture); length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);

View File

@ -22,6 +22,7 @@ using osu.Game.Overlays;
using osu.Game.Replays; using osu.Game.Replays;
using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.UI namespace osu.Game.Rulesets.UI
{ {
@ -130,7 +131,7 @@ namespace osu.Game.Rulesets.UI
protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null; protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null;
public Replay Replay { get; private set; } public Score ReplayScore { get; private set; }
/// <summary> /// <summary>
/// Whether the game is paused. Used to block user input. /// Whether the game is paused. Used to block user input.
@ -140,14 +141,14 @@ namespace osu.Game.Rulesets.UI
/// <summary> /// <summary>
/// Sets a replay to be used, overriding local input. /// Sets a replay to be used, overriding local input.
/// </summary> /// </summary>
/// <param name="replay">The replay, null for local input.</param> /// <param name="replayScore">The replay, null for local input.</param>
public virtual void SetReplay(Replay replay) public virtual void SetReplayScore(Score replayScore)
{ {
if (ReplayInputManager == null) if (ReplayInputManager == null)
throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available"); throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available");
Replay = replay; ReplayScore = replayScore;
ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; ReplayInputManager.ReplayInputHandler = replayScore != null ? CreateReplayInputHandler(replayScore.Replay) : null;
HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null; HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null;
} }
@ -302,9 +303,9 @@ namespace osu.Game.Rulesets.UI
mod.ReadFromConfig(config); mod.ReadFromConfig(config);
} }
public override void SetReplay(Replay replay) public override void SetReplayScore(Score replayScore)
{ {
base.SetReplay(replay); base.SetReplayScore(replayScore);
if (ReplayInputManager?.ReplayInputHandler != null) if (ReplayInputManager?.ReplayInputHandler != null)
ReplayInputManager.ReplayInputHandler.GamefieldToScreenSpace = Playfield.GamefieldToScreenSpace; ReplayInputManager.ReplayInputHandler.GamefieldToScreenSpace = Playfield.GamefieldToScreenSpace;

View File

@ -31,7 +31,7 @@ namespace osu.Game.Scoring
[Column(TypeName="DECIMAL(1,4)")] [Column(TypeName="DECIMAL(1,4)")]
public double Accuracy { get; set; } public double Accuracy { get; set; }
[JsonIgnore] [JsonProperty(@"pp")]
public double? PP { get; set; } public double? PP { get; set; }
[JsonProperty("max_combo")] [JsonProperty("max_combo")]

View File

@ -0,0 +1,27 @@
// Copyright (c) 2007-2018 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.Shapes;
using osu.Framework.Screens;
using osuTK.Graphics;
namespace osu.Game.Screens.Backgrounds
{
public class BackgroundScreenBlack : BackgroundScreen
{
public BackgroundScreenBlack()
{
Child = new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
};
}
protected override void OnEntering(Screen last)
{
Show();
}
}
}

View File

@ -1,9 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Screens.Backgrounds
{
public class BackgroundScreenEmpty : BackgroundScreen
{
}
}

View File

@ -17,7 +17,6 @@ namespace osu.Game.Screens.Edit.Components
public TimeInfoContainer() public TimeInfoContainer()
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
trackTimer = new OsuSpriteText trackTimer = new OsuSpriteText

View File

@ -238,11 +238,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
{ {
case Key.Right: case Key.Right:
beatDivisor.Next(); beatDivisor.Next();
OnUserChange(); OnUserChange(Current);
return true; return true;
case Key.Left: case Key.Left:
beatDivisor.Previous(); beatDivisor.Previous();
OnUserChange(); OnUserChange(Current);
return true; return true;
default: default:
return false; return false;
@ -279,7 +279,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
var xPosition = (ToLocalSpace(screenSpaceMousePosition).X - RangePadding) / UsableWidth; var xPosition = (ToLocalSpace(screenSpaceMousePosition).X - RangePadding) / UsableWidth;
CurrentNumber.Value = availableDivisors.OrderBy(d => Math.Abs(getMappedPosition(d) - xPosition)).First(); CurrentNumber.Value = availableDivisors.OrderBy(d => Math.Abs(getMappedPosition(d) - xPosition)).First();
OnUserChange(); OnUserChange(Current);
} }
private float getMappedPosition(float divisor) => (float)Math.Pow((divisor - 1) / (availableDivisors.Last() - 1), 0.90f); private float getMappedPosition(float divisor) => (float)Math.Pow((divisor - 1) / (availableDivisors.Last() - 1), 0.90f);

View File

@ -57,7 +57,7 @@ namespace osu.Game.Screens.Edit
// Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to // Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to
// the next timing point's start time // the next timing point's start time
var nextTimingPoint = ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); var nextTimingPoint = ControlPointInfo.TimingPoints.Find(t => t.Time > timingPoint.Time);
if (position > nextTimingPoint?.Time) if (position > nextTimingPoint?.Time)
position = nextTimingPoint.Time; position = nextTimingPoint.Time;
@ -123,7 +123,7 @@ namespace osu.Game.Screens.Edit
if (seekTime < timingPoint.Time && timingPoint != ControlPointInfo.TimingPoints.First()) if (seekTime < timingPoint.Time && timingPoint != ControlPointInfo.TimingPoints.First())
seekTime = timingPoint.Time; seekTime = timingPoint.Time;
var nextTimingPoint = ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time); var nextTimingPoint = ControlPointInfo.TimingPoints.Find(t => t.Time > timingPoint.Time);
if (seekTime > nextTimingPoint?.Time) if (seekTime > nextTimingPoint?.Time)
seekTime = nextTimingPoint.Time; seekTime = nextTimingPoint.Time;

View File

@ -39,7 +39,7 @@ namespace osu.Game.Screens.Menu
public override bool CursorVisible => false; public override bool CursorVisible => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty(); protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
private Bindable<bool> menuVoice; private Bindable<bool> menuVoice;
private Bindable<bool> menuMusic; private Bindable<bool> menuMusic;

View File

@ -58,6 +58,7 @@ namespace osu.Game.Screens.Menu
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Width = box_width * 2, Width = box_width * 2,
Height = 1.5f,
// align off-screen to make sure our edges don't become visible during parallax. // align off-screen to make sure our edges don't become visible during parallax.
X = -box_width, X = -box_width,
Alpha = 0, Alpha = 0,
@ -70,6 +71,7 @@ namespace osu.Game.Screens.Menu
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Width = box_width * 2, Width = box_width * 2,
Height = 1.5f,
X = box_width, X = box_width,
Alpha = 0, Alpha = 0,
Blending = BlendingMode.Additive, Blending = BlendingMode.Additive,

View File

@ -18,7 +18,7 @@ namespace osu.Game.Screens.Multi
/// <summary> /// <summary>
/// All the active <see cref="Room"/>s. /// All the active <see cref="Room"/>s.
/// </summary> /// </summary>
IBindableCollection<Room> Rooms { get; } IBindableList<Room> Rooms { get; }
/// <summary> /// <summary>
/// Creates a new <see cref="Room"/>. /// Creates a new <see cref="Room"/>.

View File

@ -1,7 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
@ -13,8 +13,9 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Components;
using osu.Game.Users; using osu.Game.Users;
@ -37,11 +38,10 @@ namespace osu.Game.Screens.Multi.Lounge.Components
private Box statusStrip; private Box statusStrip;
private UpdateableBeatmapBackgroundSprite background; private UpdateableBeatmapBackgroundSprite background;
private ParticipantCountDisplay participantCount; private ParticipantCountDisplay participantCount;
private FillFlowContainer topFlow, participantsFlow;
private OsuSpriteText name, status; private OsuSpriteText name, status;
private BeatmapTypeInfo beatmapTypeInfo; private BeatmapTypeInfo beatmapTypeInfo;
private ScrollContainer participantsScroll;
private ParticipantInfo participantInfo; private ParticipantInfo participantInfo;
private MatchParticipants participants;
[Resolved] [Resolved]
private BeatmapManager beatmaps { get; set; } private BeatmapManager beatmaps { get; set; }
@ -58,141 +58,141 @@ namespace osu.Game.Screens.Multi.Lounge.Components
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"343138"), Colour = OsuColour.FromHex(@"343138"),
}, },
topFlow = new FillFlowContainer new GridContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.Both,
AutoSizeAxes = Axes.Y, RowDimensions = new[]
Direction = FillDirection.Vertical,
Children = new Drawable[]
{ {
new Container new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Distributed),
},
Content = new[]
{
new Drawable[]
{ {
RelativeSizeAxes = Axes.X, new FillFlowContainer
Height = 200,
Masking = true,
Children = new Drawable[]
{ {
background = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }, RelativeSizeAxes = Axes.X,
new Box AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, new Container
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.5f), Color4.Black.Opacity(0)),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(20),
Children = new Drawable[]
{ {
participantCount = new ParticipantCountDisplay RelativeSizeAxes = Axes.X,
Height = 200,
Masking = true,
Children = new Drawable[]
{ {
Anchor = Anchor.TopRight, background = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both },
Origin = Anchor.TopRight, new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.5f), Color4.Black.Opacity(0)),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(20),
Children = new Drawable[]
{
participantCount = new ParticipantCountDisplay
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
name = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
TextSize = 30,
},
},
},
}, },
name = new OsuSpriteText },
statusStrip = new Box
{
RelativeSizeAxes = Axes.X,
Height = 5,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{ {
Anchor = Anchor.BottomLeft, new Box
Origin = Anchor.BottomLeft, {
TextSize = 30, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"28242d"),
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = transition_duration,
Padding = contentPadding,
Spacing = new Vector2(0f, 5f),
Children = new Drawable[]
{
status = new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
},
beatmapTypeInfo = new BeatmapTypeInfo(),
},
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = contentPadding,
Children = new Drawable[]
{
participantInfo = new ParticipantInfo(),
}, },
}, },
}, },
}, },
}, },
statusStrip = new Box new Drawable[]
{ {
RelativeSizeAxes = Axes.X, participants = new MatchParticipants
Height = 5,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{ {
new Box RelativeSizeAxes = Axes.Both,
{ }
RelativeSizeAxes = Axes.Both, }
Colour = OsuColour.FromHex(@"28242d"), }
}, }
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = transition_duration,
Padding = contentPadding,
Spacing = new Vector2(0f, 5f),
Children = new Drawable[]
{
status = new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
},
beatmapTypeInfo = new BeatmapTypeInfo(),
},
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = contentPadding,
Children = new Drawable[]
{
participantInfo = new ParticipantInfo(),
},
},
},
},
participantsScroll = new OsuScrollContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding { Top = contentPadding.Top, Left = 38, Right = 37 },
Children = new[]
{
participantsFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
LayoutDuration = transition_duration,
Spacing = new Vector2(5f),
},
},
},
}; };
participantInfo.Host.BindTo(bindings.Host); participantInfo.Host.BindTo(bindings.Host);
participantInfo.ParticipantCount.BindTo(bindings.ParticipantCount); participantInfo.ParticipantCount.BindTo(bindings.ParticipantCount);
participantInfo.Participants.BindTo(bindings.Participants); participantInfo.Participants.BindTo(bindings.Participants);
participantCount.Participants.BindTo(bindings.Participants); participantCount.Participants.BindTo(bindings.Participants);
participantCount.ParticipantCount.BindTo(bindings.ParticipantCount); participantCount.ParticipantCount.BindTo(bindings.ParticipantCount);
participantCount.MaxParticipants.BindTo(bindings.MaxParticipants); participantCount.MaxParticipants.BindTo(bindings.MaxParticipants);
beatmapTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap); beatmapTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap);
beatmapTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset); beatmapTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset);
beatmapTypeInfo.Type.BindTo(bindings.Type); beatmapTypeInfo.Type.BindTo(bindings.Type);
background.Beatmap.BindTo(bindings.CurrentBeatmap); background.Beatmap.BindTo(bindings.CurrentBeatmap);
bindings.Status.BindValueChanged(displayStatus); bindings.Status.BindValueChanged(displayStatus);
bindings.Participants.BindValueChanged(p => participantsFlow.ChildrenEnumerable = p.Select(u => new UserTile(u)));
bindings.Name.BindValueChanged(n => name.Text = n); bindings.Name.BindValueChanged(n => name.Text = n);
Room.BindValueChanged(updateRoom, true); Room.BindValueChanged(updateRoom, true);
} }
private void updateRoom(Room room) private void updateRoom(Room room)
{ {
bindings.Room = room; bindings.Room = room;
participants.Room = room;
if (room != null) if (room != null)
{ {
participantsFlow.FadeIn(transition_duration);
participantCount.FadeIn(transition_duration); participantCount.FadeIn(transition_duration);
beatmapTypeInfo.FadeIn(transition_duration); beatmapTypeInfo.FadeIn(transition_duration);
name.FadeIn(transition_duration); name.FadeIn(transition_duration);
@ -200,7 +200,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components
} }
else else
{ {
participantsFlow.FadeOut(transition_duration);
participantCount.FadeOut(transition_duration); participantCount.FadeOut(transition_duration);
beatmapTypeInfo.FadeOut(transition_duration); beatmapTypeInfo.FadeOut(transition_duration);
name.FadeOut(transition_duration); name.FadeOut(transition_duration);
@ -210,13 +209,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components
} }
} }
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
participantsScroll.Height = DrawHeight - topFlow.DrawHeight;
}
private void displayStatus(RoomStatus s) private void displayStatus(RoomStatus s)
{ {
status.Text = s.Message; status.Text = s.Message;
@ -226,39 +218,113 @@ namespace osu.Game.Screens.Multi.Lounge.Components
status.FadeColour(c, transition_duration); status.FadeColour(c, transition_duration);
} }
private class UserTile : Container, IHasTooltip
{
private readonly User user;
public string TooltipText => user.Username;
public UserTile(User user)
{
this.user = user;
Size = new Vector2(70f);
CornerRadius = 5f;
Masking = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"27252d"),
},
new UpdateableAvatar
{
RelativeSizeAxes = Axes.Both,
User = user,
},
};
}
}
private class RoomStatusNoneSelected : RoomStatus private class RoomStatusNoneSelected : RoomStatus
{ {
public override string Message => @"No Room Selected"; public override string Message => @"No Room Selected";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray8; public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray8;
} }
private class MatchParticipants : CompositeDrawable
{
private Room room;
private readonly FillFlowContainer fill;
public Room Room
{
get { return room; }
set
{
if (room == value)
return;
room = value;
updateParticipants();
}
}
public MatchParticipants()
{
Padding = new MarginPadding { Horizontal = 10 };
InternalChild = new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = fill = new FillFlowContainer
{
Spacing = new Vector2(10),
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
}
};
}
[Resolved]
private APIAccess api { get; set; }
private GetRoomScoresRequest request;
private void updateParticipants()
{
var roomId = room.RoomID.Value ?? 0;
request?.Cancel();
// nice little progressive fade
int time = 500;
foreach (var c in fill.Children)
{
c.Delay(500 - time).FadeOut(time, Easing.Out);
time = Math.Max(20, time - 20);
c.Expire();
}
if (roomId == 0) return;
request = new GetRoomScoresRequest(roomId);
request.Success += scores =>
{
if (roomId != room.RoomID.Value)
return;
fill.Clear();
foreach (var s in scores)
fill.Add(new UserTile(s.User));
fill.FadeInFromZero(1000, Easing.OutQuint);
};
api.Queue(request);
}
private class UserTile : CompositeDrawable, IHasTooltip
{
private readonly User user;
public string TooltipText => user.Username;
public UserTile(User user)
{
this.user = user;
Size = new Vector2(70f);
CornerRadius = 5f;
Masking = true;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"27252d"),
},
new UpdateableAvatar
{
RelativeSizeAxes = Axes.Both,
User = user,
},
};
}
}
}
} }
} }

View File

@ -22,7 +22,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
private readonly Bindable<Room> selectedRoom = new Bindable<Room>(); private readonly Bindable<Room> selectedRoom = new Bindable<Room>();
public IBindable<Room> SelectedRoom => selectedRoom; public IBindable<Room> SelectedRoom => selectedRoom;
private readonly IBindableCollection<Room> rooms = new BindableCollection<Room>(); private readonly IBindableList<Room> rooms = new BindableList<Room>();
private readonly FillFlowContainer<DrawableRoom> roomFlow; private readonly FillFlowContainer<DrawableRoom> roomFlow;
public IReadOnlyList<DrawableRoom> Rooms => roomFlow; public IReadOnlyList<DrawableRoom> Rooms => roomFlow;

View File

@ -16,17 +16,18 @@ namespace osu.Game.Screens.Multi.Match.Components
{ {
public Action<IEnumerable<APIRoomScoreInfo>> ScoresLoaded; public Action<IEnumerable<APIRoomScoreInfo>> ScoresLoaded;
private readonly Room room; public Room Room
public MatchLeaderboard(Room room)
{ {
this.room = room; get => bindings.Room;
set => bindings.Room = value;
} }
private readonly RoomBindings bindings = new RoomBindings();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
room.RoomID.BindValueChanged(id => bindings.RoomID.BindValueChanged(id =>
{ {
if (id == null) if (id == null)
return; return;
@ -38,10 +39,10 @@ namespace osu.Game.Screens.Multi.Match.Components
protected override APIRequest FetchScores(Action<IEnumerable<APIRoomScoreInfo>> scoresCallback) protected override APIRequest FetchScores(Action<IEnumerable<APIRoomScoreInfo>> scoresCallback)
{ {
if (room.RoomID == null) if (bindings.RoomID.Value == null)
return null; return null;
var req = new GetRoomScoresRequest(room.RoomID.Value ?? 0); var req = new GetRoomScoresRequest(bindings.RoomID.Value ?? 0);
req.Success += r => req.Success += r =>
{ {

View File

@ -165,8 +165,8 @@ namespace osu.Game.Screens.Multi.Match.Components
TimeSpan.FromHours(12), TimeSpan.FromHours(12),
//TimeSpan.FromHours(16), //TimeSpan.FromHours(16),
TimeSpan.FromHours(24), TimeSpan.FromHours(24),
//TimeSpan.FromDays(3), TimeSpan.FromDays(3),
//TimeSpan.FromDays(7) TimeSpan.FromDays(7)
} }
} }
}, },

View File

@ -71,10 +71,11 @@ namespace osu.Game.Screens.Multi.Match
{ {
new Drawable[] new Drawable[]
{ {
leaderboard = new MatchLeaderboard(room) leaderboard = new MatchLeaderboard
{ {
Padding = new MarginPadding(10), Padding = new MarginPadding(10),
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both,
Room = room
}, },
new Container new Container
{ {

View File

@ -103,8 +103,8 @@ namespace osu.Game.Screens.Multi.Ranking.Pages
public class ResultsMatchLeaderboard : MatchLeaderboard public class ResultsMatchLeaderboard : MatchLeaderboard
{ {
public ResultsMatchLeaderboard(Room room) public ResultsMatchLeaderboard(Room room)
: base(room)
{ {
Room = room;
} }
protected override bool FadeTop => true; protected override bool FadeTop => true;

View File

@ -39,6 +39,7 @@ namespace osu.Game.Screens.Multi
if (room != null) if (room != null)
{ {
RoomID.UnbindFrom(room.RoomID);
Name.UnbindFrom(room.Name); Name.UnbindFrom(room.Name);
Host.UnbindFrom(room.Host); Host.UnbindFrom(room.Host);
Status.UnbindFrom(room.Status); Status.UnbindFrom(room.Status);
@ -56,6 +57,7 @@ namespace osu.Game.Screens.Multi
if (room != null) if (room != null)
{ {
RoomID.BindTo(room.RoomID);
Name.BindTo(room.Name); Name.BindTo(room.Name);
Host.BindTo(room.Host); Host.BindTo(room.Host);
Status.BindTo(room.Status); Status.BindTo(room.Status);
@ -82,11 +84,12 @@ namespace osu.Game.Screens.Multi
currentRuleset.Value = playlistItem?.Ruleset; currentRuleset.Value = playlistItem?.Ruleset;
} }
public readonly Bindable<int?> RoomID = new Bindable<int?>();
public readonly Bindable<string> Name = new Bindable<string>(); public readonly Bindable<string> Name = new Bindable<string>();
public readonly Bindable<User> Host = new Bindable<User>(); public readonly Bindable<User> Host = new Bindable<User>();
public readonly Bindable<RoomStatus> Status = new Bindable<RoomStatus>(); public readonly Bindable<RoomStatus> Status = new Bindable<RoomStatus>();
public readonly Bindable<GameType> Type = new Bindable<GameType>(); public readonly Bindable<GameType> Type = new Bindable<GameType>();
public readonly BindableCollection<PlaylistItem> Playlist = new BindableCollection<PlaylistItem>(); public readonly BindableList<PlaylistItem> Playlist = new BindableList<PlaylistItem>();
public readonly Bindable<IEnumerable<User>> Participants = new Bindable<IEnumerable<User>>(); public readonly Bindable<IEnumerable<User>> Participants = new Bindable<IEnumerable<User>>();
public readonly Bindable<int> ParticipantCount = new Bindable<int>(); public readonly Bindable<int> ParticipantCount = new Bindable<int>();
public readonly Bindable<int?> MaxParticipants = new Bindable<int?>(); public readonly Bindable<int?> MaxParticipants = new Bindable<int?>();

View File

@ -21,8 +21,8 @@ namespace osu.Game.Screens.Multi
{ {
public event Action RoomsUpdated; public event Action RoomsUpdated;
private readonly BindableCollection<Room> rooms = new BindableCollection<Room>(); private readonly BindableList<Room> rooms = new BindableList<Room>();
public IBindableCollection<Room> Rooms => rooms; public IBindableList<Room> Rooms => rooms;
private Room currentRoom; private Room currentRoom;

View File

@ -20,6 +20,7 @@ using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -179,10 +180,13 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Alpha = 0, Alpha = 0,
}, },
new LocalSkinOverrideContainer(working.Skin) new ScalingContainer(ScalingMode.Gameplay)
{ {
RelativeSizeAxes = Axes.Both, Child = new LocalSkinOverrideContainer(working.Skin)
Child = RulesetContainer {
RelativeSizeAxes = Axes.Both,
Child = RulesetContainer
}
}, },
new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
{ {
@ -191,7 +195,10 @@ namespace osu.Game.Screens.Play
ProcessCustomClock = false, ProcessCustomClock = false,
Breaks = beatmap.Breaks Breaks = beatmap.Breaks
}, },
RulesetContainer.Cursor?.CreateProxy() ?? new Container(), new ScalingContainer(ScalingMode.Gameplay)
{
Child = RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
},
hudOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, offsetClock, adjustableClock) hudOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
{ {
Clock = Clock, // hud overlay doesn't want to use the audio clock directly Clock = Clock, // hud overlay doesn't want to use the audio clock directly
@ -285,7 +292,7 @@ namespace osu.Game.Screens.Play
if (!IsCurrentScreen) return; if (!IsCurrentScreen) return;
var score = CreateScore(); var score = CreateScore();
if (RulesetContainer.Replay == null) if (RulesetContainer.ReplayScore == null)
scoreManager.Import(score, true); scoreManager.Import(score, true);
Push(CreateResults(score)); Push(CreateResults(score));
@ -297,7 +304,7 @@ namespace osu.Game.Screens.Play
protected virtual ScoreInfo CreateScore() protected virtual ScoreInfo CreateScore()
{ {
var score = new ScoreInfo var score = RulesetContainer.ReplayScore?.ScoreInfo ?? new ScoreInfo
{ {
Beatmap = Beatmap.Value.BeatmapInfo, Beatmap = Beatmap.Value.BeatmapInfo,
Ruleset = ruleset, Ruleset = ruleset,

View File

@ -17,7 +17,7 @@ namespace osu.Game.Screens.Play
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
RulesetContainer.SetReplay(score.Replay); RulesetContainer.SetReplayScore(score);
} }
protected override ScoreInfo CreateScore() => score.ScoreInfo; protected override ScoreInfo CreateScore() => score.ScoreInfo;

View File

@ -112,6 +112,6 @@ namespace osu.Game.Screens.Play
handleBase.X = xFill; handleBase.X = xFill;
} }
protected override void OnUserChange() => OnSeek?.Invoke(Current); protected override void OnUserChange(double value) => OnSeek?.Invoke(value);
} }
} }

View File

@ -101,7 +101,6 @@ namespace osu.Game.Screens.Select
private readonly Container<DrawableCarouselItem> scrollableContent; private readonly Container<DrawableCarouselItem> scrollableContent;
public Bindable<bool> RightClickScrollingEnabled = new Bindable<bool>(); public Bindable<bool> RightClickScrollingEnabled = new Bindable<bool>();
public Bindable<RandomSelectAlgorithm> RandomAlgorithm = new Bindable<RandomSelectAlgorithm>(); public Bindable<RandomSelectAlgorithm> RandomAlgorithm = new Bindable<RandomSelectAlgorithm>();

View File

@ -297,14 +297,14 @@ namespace osu.Game.Screens.Select
/// <param name="performStartAction">Whether to trigger <see cref="OnStart"/>.</param> /// <param name="performStartAction">Whether to trigger <see cref="OnStart"/>.</param>
public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true) public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true)
{ {
// avoid attempting to continue before a selection has been obtained.
// this could happen via a user interaction while the carousel is still in a loading state.
if (Carousel.SelectedBeatmap == null) return;
// if we have a pending filter operation, we want to run it now. // if we have a pending filter operation, we want to run it now.
// it could change selection (ie. if the ruleset has been changed). // it could change selection (ie. if the ruleset has been changed).
Carousel.FlushPendingFilterOperations(); Carousel.FlushPendingFilterOperations();
// avoid attempting to continue before a selection has been obtained.
// this could happen via a user interaction while the carousel is still in a loading state.
if (Carousel.SelectedBeatmap == null) return;
if (beatmap != null) if (beatmap != null)
Carousel.SelectBeatmap(beatmap); Carousel.SelectBeatmap(beatmap);

View File

@ -327,7 +327,7 @@ namespace osu.Game.Screens.Tournament
continue; continue;
// ReSharper disable once AccessToModifiedClosure // ReSharper disable once AccessToModifiedClosure
DrawingsTeam teamToAdd = allTeams.FirstOrDefault(t => t.FullName == line); DrawingsTeam teamToAdd = allTeams.Find(t => t.FullName == line);
if (teamToAdd == null) if (teamToAdd == null)
continue; continue;

View File

@ -21,8 +21,6 @@ namespace osu.Game.Skinning
SampleChannel GetSample(string sampleName); SampleChannel GetSample(string sampleName);
TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class; TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration;
TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct;
} }
} }

View File

@ -102,7 +102,7 @@ namespace osu.Game.Skinning
string lastPiece = filename.Split('/').Last(); string lastPiece = filename.Split('/').Last();
var file = source.Files.FirstOrDefault(f => var file = source.Files.Find(f =>
string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), lastPiece, StringComparison.InvariantCultureIgnoreCase)); string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), lastPiece, StringComparison.InvariantCultureIgnoreCase));
return file?.FileInfo.StoragePath; return file?.FileInfo.StoragePath;
} }

View File

@ -30,6 +30,9 @@ namespace osu.Game.Skinning
case @"Author": case @"Author":
skin.SkinInfo.Creator = pair.Value; skin.SkinInfo.Creator = pair.Value;
break; break;
case @"CursorExpand":
skin.CursorExpand = pair.Value != "0";
break;
} }
break; break;

View File

@ -43,22 +43,14 @@ namespace osu.Game.Skinning
return fallbackSource?.GetSample(sampleName); return fallbackSource?.GetSample(sampleName);
} }
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration
{ {
TValue? val = null; TValue val;
if ((source as Skin)?.Configuration is TConfiguration conf) if ((source as Skin)?.Configuration is TConfiguration conf)
val = query?.Invoke(conf); if (beatmapSkins && (val = query.Invoke(conf)) != null)
return val;
return val ?? fallbackSource?.GetValue(query); return fallbackSource == null ? default : fallbackSource.GetValue(query);
}
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class
{
TValue val = null;
if ((source as Skin)?.Configuration is TConfiguration conf)
val = query?.Invoke(conf);
return val ?? fallbackSource?.GetValue(query);
} }
private readonly ISkinSource source; private readonly ISkinSource source;

View File

@ -22,11 +22,8 @@ namespace osu.Game.Skinning
public abstract Texture GetTexture(string componentName); public abstract Texture GetTexture(string componentName);
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration
=> Configuration is TConfiguration conf ? query?.Invoke(conf) : null; => Configuration is TConfiguration conf ? query.Invoke(conf) : default;
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct
=> Configuration is TConfiguration conf ? query?.Invoke(conf) : null;
protected Skin(SkinInfo skin) protected Skin(SkinInfo skin)
{ {

View File

@ -16,5 +16,7 @@ namespace osu.Game.Skinning
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>(); public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
public string HitCircleFont { get; set; } = "default"; public string HitCircleFont { get; set; } = "default";
public bool? CursorExpand { get; set; } = true;
} }
} }

View File

@ -116,8 +116,6 @@ namespace osu.Game.Skinning
public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName); public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName);
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class => CurrentSkin.Value.GetValue(query); public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => CurrentSkin.Value.GetValue(query);
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct => CurrentSkin.Value.GetValue(query);
} }
} }

View File

@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
namespace osu.Game.Storyboards.Drawables namespace osu.Game.Storyboards.Drawables
@ -71,7 +70,7 @@ namespace osu.Game.Storyboards.Drawables
{ {
var framePath = basePath.Replace(".", frame + "."); var framePath = basePath.Replace(".", frame + ".");
var path = beatmap.Value.BeatmapSetInfo.Files.FirstOrDefault(f => f.Filename.ToLowerInvariant() == framePath)?.FileInfo.StoragePath; var path = beatmap.Value.BeatmapSetInfo.Files.Find(f => f.Filename.ToLowerInvariant() == framePath)?.FileInfo.StoragePath;
if (path == null) if (path == null)
continue; continue;

View File

@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
namespace osu.Game.Storyboards.Drawables namespace osu.Game.Storyboards.Drawables
@ -66,7 +65,7 @@ namespace osu.Game.Storyboards.Drawables
private void load(IBindableBeatmap beatmap, TextureStore textureStore) private void load(IBindableBeatmap beatmap, TextureStore textureStore)
{ {
var spritePath = Sprite.Path.ToLowerInvariant(); var spritePath = Sprite.Path.ToLowerInvariant();
var path = beatmap.Value.BeatmapSetInfo.Files.FirstOrDefault(f => f.Filename.ToLowerInvariant() == spritePath)?.FileInfo.StoragePath; var path = beatmap.Value.BeatmapSetInfo.Files.Find(f => f.Filename.ToLowerInvariant() == spritePath)?.FileInfo.StoragePath;
if (path == null) if (path == null)
return; return;

View File

@ -78,6 +78,5 @@ namespace osu.Game.Users
[JsonProperty(@"country")] [JsonProperty(@"country")]
public int? Country; public int? Country;
} }
} }
} }

View File

@ -18,7 +18,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Framework" Version="2018.1226.0" /> <PackageReference Include="ppy.osu.Framework" Version="2019.110.1" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />