Merge master into api-as-component

This commit is contained in:
smoogipoo
2018-03-16 15:22:48 +09:00
22 changed files with 331 additions and 157 deletions

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{ {
public override void PostProcess(Beatmap<CatchHitObject> beatmap) public override void PostProcess(Beatmap<CatchHitObject> beatmap)
{ {
if (beatmap.ComboColors.Count == 0) if (beatmap.ComboColours.Count == 0)
return; return;
int index = 0; int index = 0;
@ -31,11 +31,11 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
if (obj.NewCombo) if (obj.NewCombo)
{ {
if (lastObj != null) lastObj.LastInCombo = true; if (lastObj != null) lastObj.LastInCombo = true;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count; colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count;
} }
obj.IndexInBeatmap = index++; obj.IndexInBeatmap = index++;
obj.ComboColour = beatmap.ComboColors[colourIndex]; obj.ComboColour = beatmap.ComboColours[colourIndex];
lastObj = obj; lastObj = obj;
} }

View File

@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
{ {
applyStacking(beatmap); applyStacking(beatmap);
if (beatmap.ComboColors.Count == 0) if (beatmap.ComboColours.Count == 0)
return; return;
int comboIndex = 0; int comboIndex = 0;
@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
if (obj.NewCombo) if (obj.NewCombo)
{ {
comboIndex = 0; comboIndex = 0;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count; colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count;
} }
obj.IndexInCurrentCombo = comboIndex++; obj.IndexInCurrentCombo = comboIndex++;
obj.ComboColour = beatmap.ComboColors[colourIndex]; obj.ComboColour = beatmap.ComboColours[colourIndex];
} }
} }

View File

@ -0,0 +1,33 @@
// 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;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Cursor;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCaseGameplayCursor : OsuTestCase, IProvideCursor
{
private GameplayCursor cursor;
public override IReadOnlyList<Type> RequiredTypes => new [] { typeof(CursorTrail) };
public CursorContainer Cursor => cursor;
public bool ProvidingUserCursor => true;
[BackgroundDependencyLoader]
private void load()
{
Add(cursor = new GameplayCursor { RelativeSizeAxes = Axes.Both });
}
}
}

View File

@ -3,9 +3,9 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.OpenGL.Buffers;
using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.OpenGL.Vertices;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
@ -14,11 +14,12 @@ using osu.Framework.Graphics.Textures;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Timing; using osu.Framework.Timing;
using OpenTK; using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.ES30; using OpenTK.Graphics.ES30;
namespace osu.Game.Rulesets.Osu.UI.Cursor namespace osu.Game.Rulesets.Osu.UI.Cursor
{ {
internal class CursorTrail : Drawable internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition
{ {
private int currentIndex; private int currentIndex;
@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private float time; private float time;
public override bool IsPresent => true;
private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData(); private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData();
private const int max_sprites = 2048; private const int max_sprites = 2048;
@ -96,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
const int fade_clock_reset_threshold = 1000000; const int fade_clock_reset_threshold = 1000000;
time = (float)(Time.Current - timeOffset) / 500f; time = (float)(Time.Current - timeOffset) / 300f;
if (time > fade_clock_reset_threshold) if (time > fade_clock_reset_threshold)
resetTime(); resetTime();
} }
@ -115,14 +118,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override bool OnMouseMove(InputState state) protected override bool OnMouseMove(InputState state)
{ {
Vector2 pos = state.Mouse.NativeState.Position;
if (lastPosition == null) if (lastPosition == null)
{ {
lastPosition = state.Mouse.NativeState.Position; lastPosition = pos;
resampler.AddPosition(lastPosition.Value); resampler.AddPosition(lastPosition.Value);
return base.OnMouseMove(state); return base.OnMouseMove(state);
} }
foreach (Vector2 pos2 in resampler.AddPosition(state.Mouse.NativeState.Position)) foreach (Vector2 pos2 in resampler.AddPosition(pos))
{ {
Trace.Assert(lastPosition.HasValue); Trace.Assert(lastPosition.HasValue);
@ -162,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private class TrailDrawNodeSharedData private class TrailDrawNodeSharedData
{ {
public VertexBuffer<TexturedVertex2D> VertexBuffer; public VertexBuffer<TexturedTrailVertex> VertexBuffer;
} }
private class TrailDrawNode : DrawNode private class TrailDrawNode : DrawNode
@ -188,7 +193,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
public override void Draw(Action<TexturedVertex2D> vertexAction) public override void Draw(Action<TexturedVertex2D> vertexAction)
{ {
if (Shared.VertexBuffer == null) if (Shared.VertexBuffer == null)
Shared.VertexBuffer = new QuadVertexBuffer<TexturedVertex2D>(max_sprites, BufferUsageHint.DynamicDraw); Shared.VertexBuffer = new QuadVertexBuffer<TexturedTrailVertex>(max_sprites, BufferUsageHint.DynamicDraw);
Shader.GetUniform<float>("g_FadeClock").Value = Time; Shader.GetUniform<float>("g_FadeClock").Value = Time;
@ -205,17 +210,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
int end = start; int end = start;
Vector2 pos = Parts[i].Position; Vector2 pos = Parts[i].Position;
ColourInfo colour = DrawInfo.Colour; float time = Parts[i].Time;
colour.TopLeft.Linear.A = Parts[i].Time + colour.TopLeft.Linear.A;
colour.TopRight.Linear.A = Parts[i].Time + colour.TopRight.Linear.A;
colour.BottomLeft.Linear.A = Parts[i].Time + colour.BottomLeft.Linear.A;
colour.BottomRight.Linear.A = Parts[i].Time + colour.BottomRight.Linear.A;
Texture.DrawQuad( Texture.DrawQuad(
new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y), new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y),
colour, DrawInfo.Colour,
null, null,
v => Shared.VertexBuffer.Vertices[end++] = v); v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex
{
Position = v.Position,
TexturePosition = v.TexturePosition,
Time = time + 1,
Colour = v.Colour,
});
Parts[i].WasUpdated = false; Parts[i].WasUpdated = false;
} }
@ -240,5 +247,26 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
Shader.Unbind(); Shader.Unbind();
} }
} }
[StructLayout(LayoutKind.Sequential)]
public struct TexturedTrailVertex : IEquatable<TexturedTrailVertex>, IVertex
{
[VertexMember(2, VertexAttribPointerType.Float)]
public Vector2 Position;
[VertexMember(4, VertexAttribPointerType.Float)]
public Color4 Colour;
[VertexMember(2, VertexAttribPointerType.Float)]
public Vector2 TexturePosition;
[VertexMember(1, VertexAttribPointerType.Float)]
public float Time;
public bool Equals(TexturedTrailVertex other)
{
return Position.Equals(other.Position)
&& TexturePosition.Equals(other.TexturePosition)
&& Colour.Equals(other.Colour)
&& Time.Equals(other.Time);
}
}
} }
} }

View File

@ -20,13 +20,66 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{ {
protected override Drawable CreateCursor() => new OsuCursor(); protected override Drawable CreateCursor() => new OsuCursor();
protected override Container<Drawable> Content => fadeContainer;
private readonly Container<Drawable> fadeContainer;
public GameplayCursor() public GameplayCursor()
{ {
Add(new CursorTrail { Depth = 1 }); InternalChild = fadeContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new CursorTrail { Depth = 1 }
}
};
} }
private int downCount; private int downCount;
public bool OnPressed(OsuAction action)
{
switch (action)
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
downCount++;
ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
break;
}
return false;
}
public bool OnReleased(OsuAction action)
{
switch (action)
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
if (--downCount == 0)
ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
break;
}
return false;
}
public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.
protected override void PopIn()
{
fadeContainer.FadeTo(1, 300, Easing.OutQuint);
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
}
protected override void PopOut()
{
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
}
public class OsuCursor : Container public class OsuCursor : Container
{ {
private Container cursorContainer; private Container cursorContainer;
@ -131,45 +184,5 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
cursorContainer.Scale = new Vector2(scale); cursorContainer.Scale = new Vector2(scale);
} }
} }
public bool OnPressed(OsuAction action)
{
switch (action)
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
downCount++;
ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
break;
}
return false;
}
public bool OnReleased(OsuAction action)
{
switch (action)
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
if (--downCount == 0)
ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
break;
}
return false;
}
protected override void PopIn()
{
ActiveCursor.FadeTo(1, 250, Easing.OutQuint);
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
}
protected override void PopOut()
{
ActiveCursor.FadeTo(0, 250, Easing.OutQuint);
ActiveCursor.ScaleTo(0.6f, 250, Easing.In);
}
} }
} }

View File

@ -131,6 +131,7 @@
<Compile Include="Replays\OsuReplayInputHandler.cs" /> <Compile Include="Replays\OsuReplayInputHandler.cs" />
<Compile Include="Tests\OsuBeatmapConversionTest.cs" /> <Compile Include="Tests\OsuBeatmapConversionTest.cs" />
<Compile Include="Tests\TestCaseEditor.cs" /> <Compile Include="Tests\TestCaseEditor.cs" />
<Compile Include="Tests\TestCaseGameplayCursor.cs" />
<Compile Include="Tests\TestCaseHitCircle.cs" /> <Compile Include="Tests\TestCaseHitCircle.cs" />
<Compile Include="Tests\TestCaseHitCircleHidden.cs" /> <Compile Include="Tests\TestCaseHitCircleHidden.cs" />
<Compile Include="Tests\TestCasePerformancePoints.cs" /> <Compile Include="Tests\TestCasePerformancePoints.cs" />

View File

@ -167,7 +167,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
var comboColors = decoder.Decode(stream).ComboColors; var comboColors = decoder.Decode(stream).ComboColours;
Color4[] expectedColors = Color4[] expectedColors =
{ {

View File

@ -102,9 +102,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
new Color4(255, 187, 255, 255), new Color4(255, 187, 255, 255),
new Color4(255, 177, 140, 255), new Color4(255, 177, 140, 255),
}; };
Assert.AreEqual(expected.Length, beatmap.ComboColors.Count); Assert.AreEqual(expected.Length, beatmap.ComboColours.Count);
for (int i = 0; i < expected.Length; i++) for (int i = 0; i < expected.Length; i++)
Assert.AreEqual(expected[i], beatmap.ComboColors[i]); Assert.AreEqual(expected[i], beatmap.ComboColours[i]);
} }
[Test] [Test]

View File

@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual
{ {
base.LoadComplete(); base.LoadComplete();
Add(new SkipButton(Clock.CurrentTime + 5000)); Add(new SkipOverlay(Clock.CurrentTime + 5000));
} }
} }
} }

View File

@ -9,6 +9,7 @@ using System.Linq;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.IO.Serialization; using osu.Game.IO.Serialization;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO.Serialization.Converters; using osu.Game.IO.Serialization.Converters;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
@ -16,14 +17,14 @@ namespace osu.Game.Beatmaps
/// <summary> /// <summary>
/// A Beatmap containing converted HitObjects. /// A Beatmap containing converted HitObjects.
/// </summary> /// </summary>
public class Beatmap<T> : IJsonSerializable public class Beatmap<T> : IJsonSerializable, IHasComboColours
where T : HitObject where T : HitObject
{ {
public BeatmapInfo BeatmapInfo = new BeatmapInfo(); public BeatmapInfo BeatmapInfo = new BeatmapInfo();
public ControlPointInfo ControlPointInfo = new ControlPointInfo(); public ControlPointInfo ControlPointInfo = new ControlPointInfo();
public List<BreakPeriod> Breaks = new List<BreakPeriod>(); public List<BreakPeriod> Breaks = new List<BreakPeriod>();
public List<Color4> ComboColors = new List<Color4> public List<Color4> ComboColours { get; set; } = new List<Color4>
{ {
new Color4(17, 136, 170, 255), new Color4(17, 136, 170, 255),
new Color4(102, 136, 0, 255), new Color4(102, 136, 0, 255),
@ -55,7 +56,7 @@ namespace osu.Game.Beatmaps
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo; BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
Breaks = original?.Breaks ?? Breaks; Breaks = original?.Breaks ?? Breaks;
ComboColors = original?.ComboColors ?? ComboColors; ComboColours = original?.ComboColours ?? ComboColours;
HitObjects = original?.HitObjects ?? HitObjects; HitObjects = original?.HitObjects ?? HitObjects;
if (original == null && Metadata == null) if (original == null && Metadata == null)

View File

@ -50,9 +50,14 @@ namespace osu.Game.Beatmaps
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original) protected virtual Beatmap<T> ConvertBeatmap(Beatmap original)
{ {
var beatmap = CreateBeatmap(); var beatmap = CreateBeatmap();
// todo: this *must* share logic (or directly use) Beatmap<T>'s constructor.
// right now this isn't easily possible due to generic entanglement.
beatmap.BeatmapInfo = original.BeatmapInfo; beatmap.BeatmapInfo = original.BeatmapInfo;
beatmap.ControlPointInfo = original.ControlPointInfo; beatmap.ControlPointInfo = original.ControlPointInfo;
beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList(); beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList();
beatmap.Breaks = original.Breaks;
beatmap.ComboColours = original.ComboColours;
return beatmap; return beatmap;
} }

View File

@ -0,0 +1,13 @@
// 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.Collections.Generic;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Formats
{
public interface IHasComboColours
{
List<Color4> ComboColours { get; set; }
}
}

View File

@ -0,0 +1,13 @@
// 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.Collections.Generic;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Formats
{
public interface IHasCustomColours
{
Dictionary<string, Color4> CustomColours { get; set; }
}
}

View File

@ -5,7 +5,6 @@ using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
@ -19,7 +18,6 @@ namespace osu.Game.Beatmaps.Formats
private Beatmap beatmap; private Beatmap beatmap;
private bool hasCustomColours;
private ConvertHitObjectParser parser; private ConvertHitObjectParser parser;
private LegacySampleBank defaultSampleBank; private LegacySampleBank defaultSampleBank;
@ -72,29 +70,28 @@ namespace osu.Game.Beatmaps.Formats
{ {
case Section.General: case Section.General:
handleGeneral(line); handleGeneral(line);
break; return;
case Section.Editor: case Section.Editor:
handleEditor(line); handleEditor(line);
break; return;
case Section.Metadata: case Section.Metadata:
handleMetadata(line); handleMetadata(line);
break; return;
case Section.Difficulty: case Section.Difficulty:
handleDifficulty(line); handleDifficulty(line);
break; return;
case Section.Events: case Section.Events:
handleEvents(line); handleEvents(line);
break; return;
case Section.TimingPoints: case Section.TimingPoints:
handleTimingPoints(line); handleTimingPoints(line);
break; return;
case Section.Colours:
handleColours(line);
break;
case Section.HitObjects: case Section.HitObjects:
handleHitObjects(line); handleHitObjects(line);
break; return;
} }
base.ParseLine(beatmap, section, line);
} }
private void handleGeneral(string line) private void handleGeneral(string line)
@ -364,38 +361,6 @@ namespace osu.Game.Beatmaps.Formats
} }
} }
private void handleColours(string line)
{
var pair = SplitKeyVal(line, ':');
string[] split = pair.Value.Split(',');
if (split.Length != 3)
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
byte r, g, b;
if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b))
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
if (!hasCustomColours)
{
beatmap.ComboColors.Clear();
hasCustomColours = true;
}
// Note: the combo index specified in the beatmap is discarded
if (pair.Key.StartsWith(@"Combo"))
{
beatmap.ComboColors.Add(new Color4
{
R = r / 255f,
G = g / 255f,
B = b / 255f,
A = 1f,
});
}
}
private void handleHitObjects(string line) private void handleHitObjects(string line)
{ {
// If the ruleset wasn't specified, assume the osu!standard ruleset. // If the ruleset wasn't specified, assume the osu!standard ruleset.

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Formats namespace osu.Game.Beatmaps.Formats
{ {
@ -40,7 +41,53 @@ namespace osu.Game.Beatmaps.Formats
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//"); protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//");
protected abstract void ParseLine(T output, Section section, string line); protected virtual void ParseLine(T output, Section section, string line)
{
switch (section)
{
case Section.Colours:
handleColours(output, line);
return;
}
}
private bool hasComboColours;
private void handleColours(T output, string line)
{
var pair = SplitKeyVal(line, ':');
bool isCombo = pair.Key.StartsWith(@"Combo");
string[] split = pair.Value.Split(',');
if (split.Length != 3)
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
if (!byte.TryParse(split[0], out var r) || !byte.TryParse(split[1], out var g) || !byte.TryParse(split[2], out var b))
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
Color4 colour = new Color4(r, g, b, 255);
if (isCombo)
{
if (!(output is IHasComboColours tHasComboColours)) return;
if (!hasComboColours)
{
// remove default colours.
tHasComboColours.ComboColours.Clear();
hasComboColours = true;
}
tHasComboColours.ComboColours.Add(colour);
}
else
{
if (!(output is IHasCustomColours tHasCustomColours)) return;
tHasCustomColours.CustomColours[pair.Key] = colour;
}
}
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator) protected KeyValuePair<string, string> SplitKeyVal(string line, char separator)
{ {
@ -65,6 +112,7 @@ namespace osu.Game.Beatmaps.Formats
Colours, Colours,
HitObjects, HitObjects,
Variables, Variables,
Fonts
} }
internal enum LegacySampleBank internal enum LegacySampleBank

View File

@ -46,11 +46,13 @@ namespace osu.Game.Beatmaps.Formats
{ {
case Section.Events: case Section.Events:
handleEvents(line); handleEvents(line);
break; return;
case Section.Variables: case Section.Variables:
handleVariables(line); handleVariables(line);
break; return;
} }
base.ParseLine(storyboard, section, line);
} }
private void handleEvents(string line) private void handleEvents(string line)

View File

@ -18,7 +18,7 @@ using System.Collections.Generic;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
public abstract class GameplayMenuOverlay : OverlayContainer, IRequireHighFrequencyMousePosition public abstract class GameplayMenuOverlay : OverlayContainer
{ {
private const int transition_duration = 200; private const int transition_duration = 200;
private const int button_height = 70; private const int button_height = 70;

View File

@ -164,7 +164,7 @@ namespace osu.Game.Screens.Play
Alpha = 0, Alpha = 0,
}, },
RulesetContainer, RulesetContainer,
new SkipButton(firstObjectTime) new SkipOverlay(firstObjectTime)
{ {
Clock = Clock, // skip button doesn't want to use the audio clock directly Clock = Clock, // skip button doesn't want to use the audio clock directly
ProcessCustomClock = false, ProcessCustomClock = false,

View File

@ -5,12 +5,14 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using OpenTK; using OpenTK;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Framework.Threading;
using osu.Game.Screens.Menu; using osu.Game.Screens.Menu;
using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Play.PlayerSettings;
@ -21,7 +23,6 @@ namespace osu.Game.Screens.Play
private Player player; private Player player;
private BeatmapMetadataDisplay info; private BeatmapMetadataDisplay info;
private VisualSettings visualSettings;
private bool showOverlays = true; private bool showOverlays = true;
public override bool ShowOverlaysOnEnter => showOverlays; public override bool ShowOverlaysOnEnter => showOverlays;
@ -46,7 +47,8 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
}); });
Add(visualSettings = new VisualSettings
Add(new VisualSettings
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
@ -93,7 +95,7 @@ namespace osu.Game.Screens.Play
contentIn(); contentIn();
info.Delay(750).FadeIn(500); info.Delay(750).FadeIn(500);
this.Delay(2150).Schedule(pushWhenLoaded); this.Delay(1800).Schedule(pushWhenLoaded);
} }
protected override void LogoArriving(OsuLogo logo, bool resuming) protected override void LogoArriving(OsuLogo logo, bool resuming)
@ -109,29 +111,65 @@ namespace osu.Game.Screens.Play
logo.Delay(resuming ? 0 : 500).MoveToOffset(new Vector2(0, -0.24f), 500, Easing.InOutExpo); logo.Delay(resuming ? 0 : 500).MoveToOffset(new Vector2(0, -0.24f), 500, Easing.InOutExpo);
} }
private bool weHandledMouseDown;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
weHandledMouseDown = true;
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
weHandledMouseDown = false;
return base.OnMouseUp(state, args);
}
private ScheduledDelegate pushDebounce;
private bool readyForPush => player.LoadState == LoadState.Ready && IsHovered && (!GetContainingInputManager().CurrentState.Mouse.HasAnyButtonPressed || weHandledMouseDown);
private void pushWhenLoaded() private void pushWhenLoaded()
{ {
if (player.LoadState != LoadState.Ready || visualSettings.IsHovered) if (!IsCurrentScreen) return;
try
{
if (!readyForPush)
{
// as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
// if we become unready for push during the delay.
pushDebounce?.Cancel();
pushDebounce = null;
return;
}
if (pushDebounce != null)
return;
pushDebounce = Scheduler.AddDelayed(() =>
{
contentOut();
this.Delay(250).Schedule(() =>
{
if (!IsCurrentScreen) return;
if (!Push(player))
Exit();
else
{
//By default, we want to load the player and never be returned to.
//Note that this may change if the player we load requested a re-run.
ValidForResume = false;
}
});
}, 500);
}
finally
{ {
Schedule(pushWhenLoaded); Schedule(pushWhenLoaded);
return;
} }
contentOut();
this.Delay(250).Schedule(() =>
{
if (!IsCurrentScreen) return;
if (!Push(player))
Exit();
else
{
//By default, we want to load the player and never be returned to.
//Note that this may change if the player we load requested a re-run.
ValidForResume = false;
}
});
} }
protected override bool OnExiting(Screen next) protected override bool OnExiting(Screen next)

View File

@ -5,6 +5,7 @@ using osu.Framework.Allocation;
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.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -133,5 +134,8 @@ namespace osu.Game.Screens.Play.PlayerSettings
} }
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
protected override bool OnHover(InputState state) => true;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
} }
} }

View File

@ -21,7 +21,7 @@ using osu.Game.Input.Bindings;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
public class SkipButton : OverlayContainer, IKeyBindingHandler<GlobalAction> public class SkipOverlay : OverlayContainer, IKeyBindingHandler<GlobalAction>
{ {
private readonly double startTime; private readonly double startTime;
@ -35,8 +35,9 @@ namespace osu.Game.Screens.Play
private double displayTime; private double displayTime;
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
protected override bool BlockPassThroughMouse => false;
public SkipButton(double startTime) public SkipOverlay(double startTime)
{ {
this.startTime = startTime; this.startTime = startTime;
@ -51,12 +52,6 @@ namespace osu.Game.Screens.Play
Origin = Anchor.Centre; Origin = Anchor.Centre;
} }
protected override bool OnMouseMove(InputState state)
{
fadeContainer.State = Visibility.Visible;
return base.OnMouseMove(state);
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
@ -121,15 +116,9 @@ namespace osu.Game.Screens.Play
Expire(); Expire();
} }
protected override void PopIn() protected override void PopIn() => this.FadeIn();
{
this.FadeIn();
}
protected override void PopOut() protected override void PopOut() => this.FadeOut();
{
this.FadeOut();
}
protected override void Update() protected override void Update()
{ {
@ -137,6 +126,13 @@ namespace osu.Game.Screens.Play
remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, Easing.OutQuint); remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, Easing.OutQuint);
} }
protected override bool OnMouseMove(InputState state)
{
if (!state.Mouse.HasAnyButtonPressed)
fadeContainer.State = Visibility.Visible;
return base.OnMouseMove(state);
}
public bool OnPressed(GlobalAction action) public bool OnPressed(GlobalAction action)
{ {
switch (action) switch (action)
@ -176,7 +172,7 @@ namespace osu.Game.Screens.Play
if (stateChanged) if (stateChanged)
this.FadeIn(500, Easing.OutExpo); this.FadeIn(500, Easing.OutExpo);
if (!IsHovered) if (!IsHovered && !IsDragged)
using (BeginDelayedSequence(1000)) using (BeginDelayedSequence(1000))
scheduledHide = Schedule(() => State = Visibility.Hidden); scheduledHide = Schedule(() => State = Visibility.Hidden);
break; break;
@ -194,6 +190,18 @@ namespace osu.Game.Screens.Play
base.LoadComplete(); base.LoadComplete();
State = Visibility.Visible; State = Visibility.Visible;
} }
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
scheduledHide?.Cancel();
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
State = Visibility.Visible;
return base.OnMouseUp(state, args);
}
} }
private class Button : OsuClickableContainer private class Button : OsuClickableContainer
@ -274,7 +282,7 @@ namespace osu.Game.Screens.Play
flow.TransformSpacingTo(new Vector2(5), 500, Easing.OutQuint); flow.TransformSpacingTo(new Vector2(5), 500, Easing.OutQuint);
box.FadeColour(colourHover, 500, Easing.OutQuint); box.FadeColour(colourHover, 500, Easing.OutQuint);
background.FadeTo(0.4f, 500, Easing.OutQuint); background.FadeTo(0.4f, 500, Easing.OutQuint);
return base.OnHover(state); return true;
} }
protected override void OnHoverLost(InputState state) protected override void OnHoverLost(InputState state)

View File

@ -267,6 +267,8 @@
<Compile Include="Beatmaps\DifficultyCalculator.cs" /> <Compile Include="Beatmaps\DifficultyCalculator.cs" />
<Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" /> <Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
<Compile Include="Beatmaps\Drawables\BeatmapSetCover.cs" /> <Compile Include="Beatmaps\Drawables\BeatmapSetCover.cs" />
<Compile Include="Beatmaps\Formats\IHasComboColours.cs" />
<Compile Include="Beatmaps\Formats\IHasCustomColours.cs" />
<Compile Include="Beatmaps\Formats\JsonBeatmapDecoder.cs" /> <Compile Include="Beatmaps\Formats\JsonBeatmapDecoder.cs" />
<Compile Include="Beatmaps\Formats\LegacyDecoder.cs" /> <Compile Include="Beatmaps\Formats\LegacyDecoder.cs" />
<Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" /> <Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" />
@ -809,7 +811,7 @@
<Compile Include="Screens\Play\PlayerSettings\PlayerCheckbox.cs" /> <Compile Include="Screens\Play\PlayerSettings\PlayerCheckbox.cs" />
<Compile Include="Screens\Play\PlayerSettings\PlayerSettingsGroup.cs" /> <Compile Include="Screens\Play\PlayerSettings\PlayerSettingsGroup.cs" />
<Compile Include="Screens\Play\PlayerSettings\PlayerSliderBar.cs" /> <Compile Include="Screens\Play\PlayerSettings\PlayerSliderBar.cs" />
<Compile Include="Screens\Play\SkipButton.cs" /> <Compile Include="Screens\Play\SkipOverlay.cs" />
<Compile Include="Screens\Play\SongProgress.cs" /> <Compile Include="Screens\Play\SongProgress.cs" />
<Compile Include="Screens\Play\SongProgressBar.cs" /> <Compile Include="Screens\Play\SongProgressBar.cs" />
<Compile Include="Screens\Play\SongProgressGraph.cs" /> <Compile Include="Screens\Play\SongProgressGraph.cs" />