1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-19 20:23:35 +08:00

Merge branch 'chat-mention' of https://github.com/Craftplacer/osu into chat-mention

This commit is contained in:
Craftplacer 2019-12-17 07:05:21 +01:00
commit 415defdfbd
66 changed files with 545 additions and 222 deletions

View File

@ -176,8 +176,8 @@ dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
#Style - C# 8 features
csharp_prefer_static_local_function = true:warning
csharp_prefer_simple_using_statement = true:silent
csharp_style_prefer_index_operator = false:none
csharp_style_prefer_range_operator = false:none
csharp_style_prefer_index_operator = true:warning
csharp_style_prefer_range_operator = true:warning
csharp_style_prefer_switch_expression = false:none
#Supressing roslyn built-in analyzers

View File

@ -1,5 +1,5 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.33"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.2.1"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.3.0"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();

View File

@ -1,4 +1,9 @@
{
"sdk": {
"allowPrerelease": false,
"rollForward": "minor",
"version": "3.1.100"
},
"msbuild-sdks": {
"Microsoft.Build.Traversal": "2.0.24"
}

View File

@ -53,7 +53,7 @@
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1215.0" />
</ItemGroup>
</Project>

View File

@ -51,7 +51,9 @@ namespace osu.Game.Rulesets.Catch
else if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new CatchModSuddenDeath();
if (mods.HasFlag(LegacyMods.Autoplay))
if (mods.HasFlag(LegacyMods.Cinema))
yield return new CatchModCinema();
else if (mods.HasFlag(LegacyMods.Autoplay))
yield return new CatchModAutoplay();
if (mods.HasFlag(LegacyMods.Easy))
@ -101,7 +103,7 @@ namespace osu.Game.Rulesets.Catch
case ModType.Automation:
return new Mod[]
{
new MultiMod(new CatchModAutoplay(), new ModCinema()),
new MultiMod(new CatchModAutoplay(), new CatchModCinema()),
new CatchModRelax(),
};

View File

@ -0,0 +1,21 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModCinema : ModCinema<CatchHitObject>
{
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad!" } },
Replay = new CatchAutoGenerator(beatmap).Generate(),
};
}
}

View File

@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Catch.Objects
if (value != null)
{
path.ControlPoints.AddRange(value.ControlPoints);
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
}
}

View File

@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
prevNoteTimes.RemoveAt(0);
prevNoteTimes.Add(newNoteTime);
density = (prevNoteTimes[prevNoteTimes.Count - 1] - prevNoteTimes[0]) / prevNoteTimes.Count;
density = (prevNoteTimes[^1] - prevNoteTimes[0]) / prevNoteTimes.Count;
}
private double lastTime;

View File

@ -51,7 +51,9 @@ namespace osu.Game.Rulesets.Mania
else if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new ManiaModSuddenDeath();
if (mods.HasFlag(LegacyMods.Autoplay))
if (mods.HasFlag(LegacyMods.Cinema))
yield return new ManiaModCinema();
else if (mods.HasFlag(LegacyMods.Autoplay))
yield return new ManiaModAutoplay();
if (mods.HasFlag(LegacyMods.Easy))
@ -148,7 +150,7 @@ namespace osu.Game.Rulesets.Mania
case ModType.Automation:
return new Mod[]
{
new MultiMod(new ManiaModAutoplay(), new ModCinema()),
new MultiMod(new ManiaModAutoplay(), new ManiaModCinema()),
};
case ModType.Fun:

View File

@ -0,0 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModCinema : ModCinema<ManiaHitObject>
{
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus!" } },
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
};
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -0,0 +1,2 @@
[General]
Version: 1.0

View File

@ -19,9 +19,10 @@ namespace osu.Game.Rulesets.Osu.Tests
private Skin metricsSkin;
private Skin defaultSkin;
private Skin specialSkin;
private Skin oldSkin;
protected SkinnableTestScene()
: base(2, 2)
: base(2, 3)
{
}
@ -33,6 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests
metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/special_skin"), audio, true);
oldSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/old_skin"), audio, true);
}
public void SetContents(Func<Drawable> creationFunction)
@ -41,6 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Cell(1).Child = createProvider(metricsSkin, creationFunction);
Cell(2).Child = createProvider(defaultSkin, creationFunction);
Cell(3).Child = createProvider(specialSkin, creationFunction);
Cell(4).Child = createProvider(oldSkin, creationFunction);
}
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction)

View File

@ -286,11 +286,11 @@ namespace osu.Game.Rulesets.Osu.Tests
private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
private bool assertHeadMissTailTracked() => judgementResults[judgementResults.Count - 2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
private bool assertMidSliderJudgements() => judgementResults[judgementResults.Count - 2].Type == HitResult.Great;
private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.Great;
private bool assertMidSliderJudgementFail() => judgementResults[judgementResults.Count - 2].Type == HitResult.Miss;
private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.Miss;
private ScoreAccessibleReplayPlayer currentPlayer;

View File

@ -0,0 +1,75 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
/// <summary>
/// A visualisation of the line between two <see cref="PathControlPointPiece"/>s.
/// </summary>
public class PathControlPointConnectionPiece : CompositeDrawable
{
public PathControlPoint ControlPoint;
private readonly Path path;
private readonly Slider slider;
private IBindable<Vector2> sliderPosition;
private IBindable<int> pathVersion;
public PathControlPointConnectionPiece(Slider slider, PathControlPoint controlPoint)
{
this.slider = slider;
ControlPoint = controlPoint;
Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both;
InternalChild = path = new SmoothPath
{
Anchor = Anchor.Centre,
PathRadius = 1
};
}
protected override void LoadComplete()
{
base.LoadComplete();
sliderPosition = slider.PositionBindable.GetBoundCopy();
sliderPosition.BindValueChanged(_ => updateConnectingPath());
pathVersion = slider.Path.Version.GetBoundCopy();
pathVersion.BindValueChanged(_ => updateConnectingPath());
updateConnectingPath();
}
/// <summary>
/// Updates the path connecting this control point to the next one.
/// </summary>
private void updateConnectingPath()
{
Position = slider.StackedPosition + ControlPoint.Position.Value;
path.ClearVertices();
int index = slider.Path.ControlPoints.IndexOf(ControlPoint) + 1;
if (index == 0 || index == slider.Path.ControlPoints.Count)
return;
path.AddVertex(Vector2.Zero);
path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
}
}
}

View File

@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
@ -19,6 +18,9 @@ using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
/// <summary>
/// A visualisation of a single <see cref="PathControlPoint"/> in a <see cref="Slider"/>.
/// </summary>
public class PathControlPointPiece : BlueprintPiece<Slider>
{
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
@ -28,7 +30,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
public readonly PathControlPoint ControlPoint;
private readonly Slider slider;
private readonly Path path;
private readonly Container marker;
private readonly Drawable markerRing;
@ -39,12 +40,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
private OsuColour colours { get; set; }
private IBindable<Vector2> sliderPosition;
private IBindable<int> pathVersion;
private IBindable<Vector2> controlPointPosition;
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
{
this.slider = slider;
ControlPoint = controlPoint;
Origin = Anchor.Centre;
@ -52,11 +52,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
InternalChildren = new Drawable[]
{
path = new SmoothPath
{
Anchor = Anchor.Centre,
PathRadius = 1
},
marker = new Container
{
Anchor = Anchor.Centre,
@ -96,20 +91,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
base.LoadComplete();
sliderPosition = slider.PositionBindable.GetBoundCopy();
sliderPosition.BindValueChanged(_ => updateDisplay());
sliderPosition.BindValueChanged(_ => updateMarkerDisplay());
pathVersion = slider.Path.Version.GetBoundCopy();
pathVersion.BindValueChanged(_ => updateDisplay());
controlPointPosition = ControlPoint.Position.GetBoundCopy();
controlPointPosition.BindValueChanged(_ => updateMarkerDisplay());
IsSelected.BindValueChanged(_ => updateMarkerDisplay());
updateDisplay();
}
private void updateDisplay()
{
updateMarkerDisplay();
updateConnectingPath();
}
// The connecting path is excluded from positional input
@ -189,26 +178,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
colour = Color4.White;
marker.Colour = colour;
}
/// <summary>
/// Updates the path connecting this control point to the previous one.
/// </summary>
private void updateConnectingPath()
{
path.ClearVertices();
int index = slider.Path.ControlPoints.IndexOf(ControlPoint);
if (index == -1)
return;
if (++index != slider.Path.ControlPoints.Count)
{
path.AddVertex(Vector2.Zero);
path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
}
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
}
}
}

View File

@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
internal readonly Container<PathControlPointPiece> Pieces;
private readonly Container<PathControlPointConnectionPiece> connections;
private readonly Slider slider;
private readonly bool allowSelection;
@ -42,7 +44,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
RelativeSizeAxes = Axes.Both;
InternalChild = Pieces = new Container<PathControlPointPiece> { RelativeSizeAxes = Axes.Both };
InternalChildren = new Drawable[]
{
connections = new Container<PathControlPointConnectionPiece> { RelativeSizeAxes = Axes.Both },
Pieces = new Container<PathControlPointPiece> { RelativeSizeAxes = Axes.Both }
};
}
protected override void LoadComplete()
@ -62,19 +68,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
foreach (var point in controlPoints)
{
var piece = new PathControlPointPiece(slider, point);
Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
{
if (allowSelection)
d.RequestSelection = selectPiece;
}));
if (allowSelection)
piece.RequestSelection = selectPiece;
Pieces.Add(piece);
connections.Add(new PathControlPointConnectionPiece(slider, point));
}
}
private void removeControlPoints(IEnumerable<PathControlPoint> controlPoints)
{
foreach (var point in controlPoints)
{
Pieces.RemoveAll(p => p.ControlPoint == point);
connections.RemoveAll(c => c.ControlPoint == point);
}
}
protected override bool OnClick(ClickEvent e)

View File

@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
protected override bool OnDoubleClick(DoubleClickEvent e)
{
// Todo: This should all not occur on double click, but rather if the previous control point is hovered.
segmentStart = HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 1];
segmentStart = HitObject.Path.ControlPoints[^1];
segmentStart.Type.Value = PathType.Linear;
currentSegmentLength = 1;

View File

@ -0,0 +1,25 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModCinema : ModCinema<OsuHitObject>
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray();
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "Autoplay" } },
Replay = new OsuAutoGenerator(beatmap).Generate()
};
}
}

View File

@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects
if (value != null)
{
path.ControlPoints.AddRange(value.ControlPoints);
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
}
}

View File

@ -60,7 +60,9 @@ namespace osu.Game.Rulesets.Osu
if (mods.HasFlag(LegacyMods.Autopilot))
yield return new OsuModAutopilot();
if (mods.HasFlag(LegacyMods.Autoplay))
if (mods.HasFlag(LegacyMods.Cinema))
yield return new OsuModCinema();
else if (mods.HasFlag(LegacyMods.Autoplay))
yield return new OsuModAutoplay();
if (mods.HasFlag(LegacyMods.Easy))
@ -126,7 +128,7 @@ namespace osu.Game.Rulesets.Osu
case ModType.Automation:
return new Mod[]
{
new MultiMod(new OsuModAutoplay(), new ModCinema()),
new MultiMod(new OsuModAutoplay(), new OsuModCinema()),
new OsuModRelax(),
new OsuModAutopilot(),
};

View File

@ -156,9 +156,9 @@ namespace osu.Game.Rulesets.Osu.Replays
// TODO: Shouldn't the spinner always spin in the same direction?
if (h is Spinner)
{
calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[Frames.Count - 1]).Position, out startPosition, out spinnerDirection);
calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection);
Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[Frames.Count - 1]).Position;
Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[^1]).Position;
if (spinCentreOffset.Length > SPIN_RADIUS)
{
@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.Osu.Replays
private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing)
{
OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[Frames.Count - 1];
OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[^1];
// Wait until Auto could "see and react" to the next note.
double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime);
@ -363,7 +363,7 @@ namespace osu.Game.Rulesets.Osu.Replays
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
if (Frames[Frames.Count - 1].Time <= endFrame.Time)
if (Frames[^1].Time <= endFrame.Time)
AddFrameToReplay(endFrame);
}

View File

@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private void addPart(Vector2 screenSpacePosition)
{
parts[currentIndex].Position = screenSpacePosition;
parts[currentIndex].Time = time;
parts[currentIndex].Time = time + 1;
++parts[currentIndex].InvalidationID;
currentIndex = (currentIndex + 1) % max_sprites;
@ -201,7 +201,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly TrailPart[] parts = new TrailPart[max_sprites];
private Vector2 size;
private readonly TrailBatch vertexBatch = new TrailBatch(max_sprites, 1);
private readonly QuadBatch<TexturedTrailVertex> vertexBatch = new QuadBatch<TexturedTrailVertex>(max_sprites, 1);
public TrailDrawNode(CursorTrail source)
: base(source)
@ -227,23 +227,52 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
shader.Bind();
shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time);
for (int i = 0; i < parts.Length; ++i)
texture.TextureGL.Bind();
RectangleF textureRect = texture.GetTextureRect();
foreach (var part in parts)
{
if (parts[i].InvalidationID == -1)
if (part.InvalidationID == -1)
continue;
vertexBatch.DrawTime = parts[i].Time;
if (time - part.Time >= 1)
continue;
Vector2 pos = parts[i].Position;
vertexBatch.Add(new TexturedTrailVertex
{
Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y + size.Y / 2),
TexturePosition = textureRect.BottomLeft,
Colour = DrawColourInfo.Colour.BottomLeft.Linear,
Time = part.Time
});
DrawQuad(
texture,
new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
DrawColourInfo.Colour,
null,
vertexBatch.AddAction);
vertexBatch.Add(new TexturedTrailVertex
{
Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y + size.Y / 2),
TexturePosition = textureRect.BottomRight,
Colour = DrawColourInfo.Colour.BottomRight.Linear,
Time = part.Time
});
vertexBatch.Add(new TexturedTrailVertex
{
Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y - size.Y / 2),
TexturePosition = textureRect.TopRight,
Colour = DrawColourInfo.Colour.TopRight.Linear,
Time = part.Time
});
vertexBatch.Add(new TexturedTrailVertex
{
Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y - size.Y / 2),
TexturePosition = textureRect.TopLeft,
Colour = DrawColourInfo.Colour.TopLeft.Linear,
Time = part.Time
});
}
vertexBatch.Draw();
shader.Unbind();
}
@ -253,25 +282,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
vertexBatch.Dispose();
}
// Todo: This shouldn't exist, but is currently used to reduce allocations by caching variable-capturing closures.
private class TrailBatch : QuadBatch<TexturedTrailVertex>
{
public new readonly Action<TexturedVertex2D> AddAction;
public float DrawTime;
public TrailBatch(int size, int maxBuffers)
: base(size, maxBuffers)
{
AddAction = v => Add(new TexturedTrailVertex
{
Position = v.Position,
TexturePosition = v.TexturePosition,
Time = DrawTime + 1,
Colour = v.Colour,
});
}
}
}
[StructLayout(LayoutKind.Sequential)]

View File

@ -0,0 +1,21 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Replays;
using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModCinema : ModCinema<TaikoHitObject>
{
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "mekkadosu!" } },
Replay = new TaikoAutoGenerator(beatmap).Generate(),
};
}
}

View File

@ -50,7 +50,9 @@ namespace osu.Game.Rulesets.Taiko
else if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new TaikoModSuddenDeath();
if (mods.HasFlag(LegacyMods.Autoplay))
if (mods.HasFlag(LegacyMods.Cinema))
yield return new TaikoModCinema();
else if (mods.HasFlag(LegacyMods.Autoplay))
yield return new TaikoModAutoplay();
if (mods.HasFlag(LegacyMods.Easy))
@ -100,7 +102,7 @@ namespace osu.Game.Rulesets.Taiko
case ModType.Automation:
return new Mod[]
{
new MultiMod(new TaikoModAutoplay(), new ModCinema()),
new MultiMod(new TaikoModAutoplay(), new TaikoModCinema()),
new TaikoModRelax(),
};

View File

@ -88,6 +88,20 @@ namespace osu.Game.Tests.Visual.Background
AddUntilStep("not lightened", () => userDimContainer.DimEqual(test_user_dim));
}
[Test]
public void TestIgnoreUserSettings()
{
AddStep("set dim level 0.6", () => userDimContainer.UserDimLevel.Value = test_user_dim);
AddUntilStep("dim reached", () => userDimContainer.DimEqual(test_user_dim));
AddStep("ignore settings", () => userDimContainer.IgnoreUserSettings.Value = true);
AddUntilStep("no dim", () => userDimContainer.DimEqual(0));
AddStep("set break", () => isBreakTime.Value = true);
AddAssert("no dim", () => userDimContainer.DimEqual(0));
AddStep("clear break", () => isBreakTime.Value = false);
AddAssert("no dim", () => userDimContainer.DimEqual(0));
}
private class TestUserDimContainer : UserDimContainer
{
public bool DimEqual(float expectedDimLevel) => Content.Colour == OsuColour.Gray(1f - expectedDimLevel);

View File

@ -0,0 +1,81 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneHUDOverlay : ManualInputManagerTestScene
{
private HUDOverlay hudOverlay;
private Drawable hideTarget => hudOverlay.KeyCounter; // best way of checking hideTargets without exposing.
[Resolved]
private OsuConfigManager config { get; set; }
[Test]
public void TestShownByDefault()
{
createNew();
AddAssert("showhud is set", () => hudOverlay.ShowHud.Value);
AddAssert("hidetarget is visible", () => hideTarget.IsPresent);
AddAssert("pause button is visible", () => hudOverlay.HoldToQuit.IsPresent);
}
[Test]
public void TestFadesInOnLoadComplete()
{
float? initialAlpha = null;
createNew(h => h.OnLoadComplete += _ => initialAlpha = hideTarget.Alpha);
AddUntilStep("wait for load", () => hudOverlay.IsAlive);
AddAssert("initial alpha was less than 1", () => initialAlpha != null && initialAlpha < 1);
}
[Test]
public void TestHideExternally()
{
createNew();
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
AddUntilStep("hidetarget is hidden", () => !hideTarget.IsPresent);
AddAssert("pause button is still visible", () => hudOverlay.HoldToQuit.IsPresent);
}
[Test]
public void TestExternalHideDoesntAffectConfig()
{
bool originalConfigValue = false;
createNew();
AddStep("get original config value", () => originalConfigValue = config.Get<bool>(OsuSetting.ShowInterface));
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
AddAssert("config unchanged", () => originalConfigValue == config.Get<bool>(OsuSetting.ShowInterface));
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
AddAssert("config unchanged", () => originalConfigValue == config.Get<bool>(OsuSetting.ShowInterface));
}
private void createNew(Action<HUDOverlay> action = null)
{
AddStep("create overlay", () =>
{
Child = hudOverlay = new HUDOverlay(null, null, Array.Empty<Mod>());
action?.Invoke(hudOverlay);
});
}
}
}

View File

@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Online
AddStep("set second set", () => details.BeatmapSet = secondSet);
AddAssert("ratings set", () => details.Ratings.Metrics == secondSet.Metrics);
BeatmapSetInfo createSet() => new BeatmapSetInfo
static BeatmapSetInfo createSet() => new BeatmapSetInfo
{
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray() },
Beatmaps = new List<BeatmapInfo>

View File

@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private TimingControlPoint getNextTimingPoint(TimingControlPoint current)
{
if (timingPoints[timingPoints.Count - 1] == current)
if (timingPoints[^1] == current)
return current;
int index = timingPoints.IndexOf(current); // -1 means that this is a "default beat"
@ -169,7 +169,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
if (timingPoints.Count == 0) return 0;
if (timingPoints[timingPoints.Count - 1] == current)
if (timingPoints[^1] == current)
return (int)Math.Ceiling((Beatmap.Value.Track.Length - current.Time) / current.BeatLength);
return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength);

View File

@ -195,8 +195,8 @@ namespace osu.Game.Beatmaps.ControlPoints
if (time < list[0].Time)
return null;
if (time >= list[list.Count - 1].Time)
return list[list.Count - 1];
if (time >= list[^1].Time)
return list[^1];
int l = 0;
int r = list.Count - 2;

View File

@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps.Formats
if (line.StartsWith(@"[", StringComparison.Ordinal) && line.EndsWith(@"]", StringComparison.Ordinal))
{
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
if (!Enum.TryParse(line[1..^1], out section))
{
Logger.Log($"Unknown section \"{line}\" in \"{output}\"");
section = Section.None;

View File

@ -37,7 +37,7 @@ namespace osu.Game.Graphics.Containers
foreach (var link in links)
{
AddText(text.Substring(previousLinkEnd, link.Index - previousLinkEnd));
AddText(text[previousLinkEnd..link.Index]);
AddLink(text.Substring(link.Index, link.Length), link.Action, link.Argument ?? link.Url);
previousLinkEnd = link.Index + link.Length;
}

View File

@ -83,6 +83,15 @@ namespace osu.Game.Graphics.Containers
return base.OnDragEnd(e);
}
protected override bool OnScroll(ScrollEvent e)
{
// allow for controlling volume when alt is held.
// mostly for compatibility with osu-stable.
if (e.AltPressed) return false;
return base.OnScroll(e);
}
protected override ScrollbarContainer CreateScrollbar(Direction direction) => new OsuScrollbar(direction);
protected class OsuScrollbar : ScrollbarContainer

View File

@ -28,6 +28,11 @@ namespace osu.Game.Graphics.Containers
/// </summary>
public readonly Bindable<bool> EnableUserDim = new Bindable<bool>(true);
/// <summary>
/// Whether or not user-configured settings relating to brightness of elements should be ignored
/// </summary>
public readonly Bindable<bool> IgnoreUserSettings = new Bindable<bool>();
/// <summary>
/// Whether or not the storyboard loaded should completely hide the background behind it.
/// </summary>
@ -53,7 +58,8 @@ namespace osu.Game.Graphics.Containers
protected Bindable<bool> ShowVideo { get; private set; }
private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0;
protected float DimLevel => Math.Max(EnableUserDim.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0);
protected float DimLevel => Math.Max(EnableUserDim.Value && !IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0);
protected override Container<Drawable> Content => dimContent;
@ -82,6 +88,7 @@ namespace osu.Game.Graphics.Containers
ShowStoryboard.ValueChanged += _ => UpdateVisuals();
ShowVideo.ValueChanged += _ => UpdateVisuals();
StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals();
IgnoreUserSettings.ValueChanged += _ => UpdateVisuals();
}
protected override void LoadComplete()

View File

@ -115,11 +115,7 @@ namespace osu.Game.Overlays.Chat.Selection
Font = OsuFont.GetFont(size: 20),
Shadow = false,
},
search = new HeaderSearchTextBox
{
RelativeSizeAxes = Axes.X,
PlaceholderText = @"Search",
},
search = new HeaderSearchTextBox { RelativeSizeAxes = Axes.X },
},
},
},

View File

@ -0,0 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Screens.Play;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for a mod which can temporarily override the <see cref="Player"/> settings.
/// </summary>
public interface IApplicableToPlayer : IApplicableMod
{
void ApplyToPlayer(Player player);
}
}

View File

@ -3,15 +3,46 @@
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
namespace osu.Game.Rulesets.Mods
{
public class ModCinema : ModAutoplay
public abstract class ModCinema<T> : ModCinema, IApplicableToDrawableRuleset<T>
where T : HitObject
{
public virtual void ApplyToDrawableRuleset(DrawableRuleset<T> drawableRuleset)
{
drawableRuleset.SetReplayScore(CreateReplayScore(drawableRuleset.Beatmap));
// AlwaysPresent required for hitsounds
drawableRuleset.Playfield.AlwaysPresent = true;
drawableRuleset.Playfield.Hide();
}
}
public class ModCinema : ModAutoplay, IApplicableToHUD, IApplicableToPlayer
{
public override string Name => "Cinema";
public override string Acronym => "CN";
public override bool HasImplementation => false;
public override IconUsage Icon => OsuIcon.ModCinema;
public override string Description => "Watch the video without visual distractions.";
public void ApplyToHUD(HUDOverlay overlay)
{
overlay.ShowHud.Value = false;
overlay.ShowHud.Disabled = true;
}
public void ApplyToPlayer(Player player)
{
player.Background.EnableUserDim.Value = false;
player.DimmableVideo.IgnoreUserSettings.Value = true;
player.DimmableStoryboard.IgnoreUserSettings.Value = true;
player.BreakOverlay.Hide();
}
}
}

View File

@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
result = CreateSlider(pos, combo, comboOffset, convertControlPoints(points, pathType), length, repeatCount, nodeSamples);
// The samples are played when the slider ends, which is the last node
result.Samples = nodeSamples[nodeSamples.Count - 1];
result.Samples = nodeSamples[^1];
}
else if (type.HasFlag(ConvertHitObjectType.Spinner))
{
@ -279,7 +279,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
{
if (vertices[i] == vertices[i - 1])
{
points[points.Count - 1].Type.Value = type;
points[^1].Type.Value = type;
continue;
}

View File

@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Objects
get
{
ensureValid();
return cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1];
return cumulativeLength.Count == 0 ? 0 : cumulativeLength[^1];
}
}
@ -251,7 +251,7 @@ namespace osu.Game.Rulesets.Objects
if (calculatedLength > expectedDistance)
{
// The path will be shortened further, in which case we should trim any more unnecessary lengths and their associated path segments
while (cumulativeLength.Count > 0 && cumulativeLength[cumulativeLength.Count - 1] >= expectedDistance)
while (cumulativeLength.Count > 0 && cumulativeLength[^1] >= expectedDistance)
{
cumulativeLength.RemoveAt(cumulativeLength.Count - 1);
calculatedPath.RemoveAt(pathEndIndex--);
@ -269,7 +269,7 @@ namespace osu.Game.Rulesets.Objects
// The direction of the segment to shorten or lengthen
Vector2 dir = (calculatedPath[pathEndIndex] - calculatedPath[pathEndIndex - 1]).Normalized();
calculatedPath[pathEndIndex] = calculatedPath[pathEndIndex - 1] + dir * (float)(expectedDistance - cumulativeLength[cumulativeLength.Count - 1]);
calculatedPath[pathEndIndex] = calculatedPath[pathEndIndex - 1] + dir * (float)(expectedDistance - cumulativeLength[^1]);
cumulativeLength.Add(expectedDistance);
}
}

View File

@ -33,14 +33,14 @@ namespace osu.Game.Screens.Play
base.LoadComplete();
}
protected override bool ShowDimContent => ShowStoryboard.Value && DimLevel < 1;
protected override bool ShowDimContent => IgnoreUserSettings.Value || (ShowStoryboard.Value && DimLevel < 1);
private void initializeStoryboard(bool async)
{
if (drawableStoryboard != null)
return;
if (!ShowStoryboard.Value)
if (!ShowStoryboard.Value && !IgnoreUserSettings.Value)
return;
drawableStoryboard = storyboard.CreateDrawable();

View File

@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play
base.LoadComplete();
}
protected override bool ShowDimContent => ShowVideo.Value && DimLevel < 1;
protected override bool ShowDimContent => IgnoreUserSettings.Value || (ShowVideo.Value && DimLevel < 1);
private void initializeVideo(bool async)
{
@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play
if (drawableVideo != null)
return;
if (!ShowVideo.Value)
if (!ShowVideo.Value && !IgnoreUserSettings.Value)
return;
drawableVideo = new DrawableVideo(video);

View File

@ -31,7 +31,8 @@ namespace osu.Game.Screens.Play.HUD
RelativeSizeAxes = Axes.Both;
processor.NewJudgement += onNewJudgement;
if (processor != null)
processor.NewJudgement += onNewJudgement;
}
[BackgroundDependencyLoader]
@ -96,7 +97,9 @@ namespace osu.Game.Screens.Play.HUD
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
processor.NewJudgement -= onNewJudgement;
if (processor != null)
processor.NewJudgement -= onNewJudgement;
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
@ -23,8 +24,8 @@ namespace osu.Game.Screens.Play
{
public class HUDOverlay : Container
{
private const int duration = 250;
private const Easing easing = Easing.OutQuint;
private const float fade_duration = 400;
private const Easing fade_easing = Easing.Out;
public readonly KeyCounterDisplay KeyCounter;
public readonly RollingCounter<int> ComboCounter;
@ -43,8 +44,15 @@ namespace osu.Game.Screens.Play
private readonly DrawableRuleset drawableRuleset;
private readonly IReadOnlyList<Mod> mods;
private Bindable<bool> showHud;
/// <summary>
/// Whether the elements that can optionally be hidden should be visible.
/// </summary>
public Bindable<bool> ShowHud { get; } = new BindableBool();
private Bindable<bool> configShowHud;
private readonly Container visibilityContainer;
private readonly BindableBool replayLoaded = new BindableBool();
private static bool hasShownNotificationOnce;
@ -53,6 +61,8 @@ namespace osu.Game.Screens.Play
private readonly Container topScoreContainer;
private IEnumerable<Drawable> hideTargets => new Drawable[] { visibilityContainer, KeyCounter };
public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
{
this.scoreProcessor = scoreProcessor;
@ -68,13 +78,12 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
HealthDisplay = CreateHealthDisplay(),
topScoreContainer = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
AutoSizeDuration = 200,
AutoSizeEasing = Easing.Out,
Children = new Drawable[]
{
AccuracyCounter = CreateAccuracyCounter(),
@ -82,19 +91,20 @@ namespace osu.Game.Screens.Play
ComboCounter = CreateComboCounter(),
},
},
HealthDisplay = CreateHealthDisplay(),
Progress = CreateProgress(),
ModDisplay = CreateModsContainer(),
HitErrorDisplay = CreateHitErrorDisplayOverlay(),
PlayerSettingsOverlay = CreatePlayerSettingsOverlay(),
}
},
PlayerSettingsOverlay = CreatePlayerSettingsOverlay(),
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Position = -new Vector2(5, TwoLayerButton.SIZE_RETRACTED.Y),
AutoSizeAxes = Axes.Both,
LayoutDuration = fade_duration / 2,
LayoutEasing = fade_easing,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
@ -108,34 +118,24 @@ namespace osu.Game.Screens.Play
[BackgroundDependencyLoader(true)]
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay)
{
BindProcessor(scoreProcessor);
BindDrawableRuleset(drawableRuleset);
if (scoreProcessor != null)
BindProcessor(scoreProcessor);
Progress.Objects = drawableRuleset.Objects;
Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value;
Progress.RequestSeek = time => RequestSeek(time);
Progress.ReferenceClock = drawableRuleset.FrameStableClock;
if (drawableRuleset != null)
{
BindDrawableRuleset(drawableRuleset);
Progress.Objects = drawableRuleset.Objects;
Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value;
Progress.RequestSeek = time => RequestSeek(time);
Progress.ReferenceClock = drawableRuleset.FrameStableClock;
}
ModDisplay.Current.Value = mods;
showHud = config.GetBindable<bool>(OsuSetting.ShowInterface);
showHud.BindValueChanged(visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration, easing), true);
configShowHud = config.GetBindable<bool>(OsuSetting.ShowInterface);
ShowHealthbar.BindValueChanged(healthBar =>
{
if (healthBar.NewValue)
{
HealthDisplay.FadeIn(duration, easing);
topScoreContainer.MoveToY(30, duration, easing);
}
else
{
HealthDisplay.FadeOut(duration, easing);
topScoreContainer.MoveToY(0, duration, easing);
}
}, true);
if (!showHud.Value && !hasShownNotificationOnce)
if (!configShowHud.Value && !hasShownNotificationOnce)
{
hasShownNotificationOnce = true;
@ -144,12 +144,39 @@ namespace osu.Game.Screens.Play
Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab."
});
}
// start all elements hidden
hideTargets.ForEach(d => d.Hide());
}
public override void Hide() => throw new InvalidOperationException($"{nameof(HUDOverlay)} should not be hidden as it will remove the ability of a user to quit. Use {nameof(ShowHud)} instead.");
protected override void LoadComplete()
{
base.LoadComplete();
ShowHud.BindValueChanged(visible => hideTargets.ForEach(d => d.FadeTo(visible.NewValue ? 1 : 0, fade_duration, fade_easing)));
ShowHealthbar.BindValueChanged(healthBar =>
{
if (healthBar.NewValue)
{
HealthDisplay.FadeIn(fade_duration, fade_easing);
topScoreContainer.MoveToY(30, fade_duration, fade_easing);
}
else
{
HealthDisplay.FadeOut(fade_duration, fade_easing);
topScoreContainer.MoveToY(0, fade_duration, fade_easing);
}
}, true);
configShowHud.BindValueChanged(visible =>
{
if (!ShowHud.Disabled)
ShowHud.Value = visible.NewValue;
}, true);
replayLoaded.BindValueChanged(replayLoadedValueChanged, true);
}
@ -189,7 +216,7 @@ namespace osu.Game.Screens.Play
switch (e.Key)
{
case Key.Tab:
showHud.Value = !showHud.Value;
configShowHud.Value = !configShowHud.Value;
return true;
}
}
@ -257,7 +284,7 @@ namespace osu.Game.Screens.Play
Margin = new MarginPadding { Top = 20, Right = 10 },
};
protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.FirstAvailableHitWindows);
protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset?.FirstAvailableHitWindows);
protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay();

View File

@ -69,7 +69,7 @@ namespace osu.Game.Screens.Play
private SampleChannel sampleRestart;
private BreakOverlay breakOverlay;
public BreakOverlay BreakOverlay;
protected ScoreProcessor ScoreProcessor { get; private set; }
protected DrawableRuleset DrawableRuleset { get; private set; }
@ -80,8 +80,8 @@ namespace osu.Game.Screens.Play
protected GameplayClockContainer GameplayClockContainer { get; private set; }
protected DimmableStoryboard DimmableStoryboard { get; private set; }
protected DimmableVideo DimmableVideo { get; private set; }
public DimmableStoryboard DimmableStoryboard { get; private set; }
public DimmableVideo DimmableVideo { get; private set; }
[Cached]
[Cached(Type = typeof(IBindable<IReadOnlyList<Mod>>))]
@ -154,7 +154,7 @@ namespace osu.Game.Screens.Play
foreach (var mod in Mods.Value.OfType<IApplicableToScoreProcessor>())
mod.ApplyToScoreProcessor(ScoreProcessor);
breakOverlay.IsBreakTime.ValueChanged += _ => updatePauseOnFocusLostState();
BreakOverlay.IsBreakTime.ValueChanged += _ => updatePauseOnFocusLostState();
}
private void addUnderlayComponents(Container target)
@ -188,7 +188,7 @@ namespace osu.Game.Screens.Play
{
target.AddRange(new[]
{
breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, DrawableRuleset.GameplayStartTime, ScoreProcessor)
BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, DrawableRuleset.GameplayStartTime, ScoreProcessor)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -253,7 +253,7 @@ namespace osu.Game.Screens.Play
private void updatePauseOnFocusLostState() =>
HUDOverlay.HoldToQuit.PauseOnFocusLost = PauseOnFocusLost
&& !DrawableRuleset.HasReplayLoaded.Value
&& !breakOverlay.IsBreakTime.Value;
&& !BreakOverlay.IsBreakTime.Value;
private IBeatmap loadPlayableBeatmap()
{
@ -478,7 +478,7 @@ namespace osu.Game.Screens.Play
PauseOverlay.Hide();
// breaks and time-based conditions may allow instant resume.
if (breakOverlay.IsBreakTime.Value)
if (BreakOverlay.IsBreakTime.Value)
completeResume();
else
DrawableRuleset.RequestResume(completeResume);
@ -512,9 +512,9 @@ namespace osu.Game.Screens.Play
Background.BlurAmount.Value = 0;
// bind component bindables.
Background.IsBreakTime.BindTo(breakOverlay.IsBreakTime);
DimmableStoryboard.IsBreakTime.BindTo(breakOverlay.IsBreakTime);
DimmableVideo.IsBreakTime.BindTo(breakOverlay.IsBreakTime);
Background.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
DimmableStoryboard.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
DimmableVideo.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
@ -524,6 +524,9 @@ namespace osu.Game.Screens.Play
GameplayClockContainer.Restart();
GameplayClockContainer.FadeInFromZero(750, Easing.OutQuint);
foreach (var mod in Mods.Value.OfType<IApplicableToPlayer>())
mod.ApplyToPlayer(this);
foreach (var mod in Mods.Value.OfType<IApplicableToHUD>())
mod.ApplyToHUD(HUDOverlay);
}

View File

@ -9,6 +9,6 @@ namespace osu.Game.Screens.Play
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
protected new BackgroundScreenBeatmap Background => (BackgroundScreenBeatmap)base.Background;
public new BackgroundScreenBeatmap Background => (BackgroundScreenBeatmap)base.Background;
}
}

View File

@ -587,13 +587,16 @@ namespace osu.Game.Screens.Select
switch (d)
{
case DrawableCarouselBeatmapSet set:
{
lastSet = set;
set.MoveToX(set.Item.State.Value == CarouselItemState.Selected ? -100 : 0, 500, Easing.OutExpo);
set.MoveToY(currentY, 750, Easing.OutExpo);
break;
}
case DrawableCarouselBeatmap beatmap:
{
if (beatmap.Item.State.Value == CarouselItemState.Selected)
scrollTarget = currentY + beatmap.DrawHeight / 2 - DrawHeight / 2;
@ -619,6 +622,7 @@ namespace osu.Game.Screens.Select
}
break;
}
}
}

View File

@ -3,77 +3,39 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.IO.Stores;
using osu.Game.Database;
namespace osu.Game.Skinning
{
public class LegacySkinResourceStore<T> : IResourceStore<byte[]>
public class LegacySkinResourceStore<T> : ResourceStore<byte[]>
where T : INamedFileInfo
{
private readonly IHasFiles<T> source;
private readonly IResourceStore<byte[]> underlyingStore;
private string getPathForFile(string filename)
{
if (source.Files == null)
return null;
bool hasExtension = filename.Contains('.');
var file = source.Files.Find(f =>
string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), filename, StringComparison.InvariantCultureIgnoreCase));
return file?.FileInfo.StoragePath;
}
public LegacySkinResourceStore(IHasFiles<T> source, IResourceStore<byte[]> underlyingStore)
: base(underlyingStore)
{
this.source = source;
this.underlyingStore = underlyingStore;
}
public Stream GetStream(string name)
protected override IEnumerable<string> GetFilenames(string name)
{
string path = getPathForFile(name);
return path == null ? null : underlyingStore.GetStream(path);
}
if (source.Files == null)
yield break;
public IEnumerable<string> GetAvailableResources() => source.Files.Select(f => f.Filename);
byte[] IResourceStore<byte[]>.Get(string name) => GetAsync(name).Result;
public Task<byte[]> GetAsync(string name)
{
string path = getPathForFile(name);
return path == null ? Task.FromResult<byte[]>(null) : underlyingStore.GetAsync(path);
}
#region IDisposable Support
private bool isDisposed;
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
foreach (var filename in base.GetFilenames(name))
{
isDisposed = true;
var path = getPathForFile(filename);
if (path != null)
yield return path;
}
}
~LegacySkinResourceStore()
{
Dispose(false);
}
private string getPathForFile(string filename) =>
source.Files.Find(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
public override IEnumerable<string> GetAvailableResources() => source.Files.Select(f => f.Filename);
}
}

View File

@ -22,7 +22,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1215.0" />
<PackageReference Include="Sentry" Version="1.2.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />

View File

@ -73,7 +73,7 @@
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1215.0" />
</ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->

View File

@ -215,11 +215,12 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedMethodReturnValue_002ELocal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedParameter_002EGlobal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedParameter_002ELocal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedType_002EGlobal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseAwaitUsing/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseCollectionCountProperty/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseFormatSpecifierInFormatString/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseFormatSpecifierInInterpolation/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseIndexFromEndExpression/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseIndexFromEndExpression/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseNameofExpression/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseNegatedPatternMatching/@EntryIndexedValue"></s:String>
<s:Boolean x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseNegatedPatternMatching/@EntryIndexRemoved">True</s:Boolean>