Merge branch 'reset-dho-lifetimes' into fix-catch-rewind
@ -1,5 +1,7 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<OutputPath>bin\$(Configuration)</OutputPath>
|
<OutputPath>bin\$(Configuration)</OutputPath>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
@ -35,7 +37,7 @@
|
|||||||
<DebugType>None</DebugType>
|
<DebugType>None</DebugType>
|
||||||
<Optimize>True</Optimize>
|
<Optimize>True</Optimize>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<EnableLLVM>true</EnableLLVM>
|
<EnableLLVM>true</EnableLLVM>
|
||||||
<AndroidManagedSymbols>false</AndroidManagedSymbols>
|
<AndroidManagedSymbols>false</AndroidManagedSymbols>
|
||||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
||||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||||
@ -61,6 +63,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.904.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.904.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.905.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.911.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -75,11 +75,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Miss:
|
case ArmedState.Miss:
|
||||||
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire();
|
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
this.FadeOut().Expire();
|
this.FadeOut();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
@ -12,6 +13,7 @@ using osu.Game.Tests.Visual;
|
|||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
|
[HeadlessTest]
|
||||||
public class TestSceneAutoGeneration : OsuTestScene
|
public class TestSceneAutoGeneration : OsuTestScene
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
|
62
osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneHitExplosion : OsuTestScene
|
||||||
|
{
|
||||||
|
private ScrollingTestContainer scrolling;
|
||||||
|
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(DrawableNote),
|
||||||
|
typeof(DrawableManiaHitObject),
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Child = scrolling = new ScrollingTestContainer(ScrollingDirection.Down)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativePositionAxes = Axes.Y,
|
||||||
|
Y = -0.25f,
|
||||||
|
Size = new Vector2(Column.COLUMN_WIDTH, NotePiece.NOTE_HEIGHT),
|
||||||
|
};
|
||||||
|
|
||||||
|
int runcount = 0;
|
||||||
|
|
||||||
|
AddRepeatStep("explode", () =>
|
||||||
|
{
|
||||||
|
runcount++;
|
||||||
|
|
||||||
|
if (runcount % 15 > 12)
|
||||||
|
return;
|
||||||
|
|
||||||
|
scrolling.AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
new HitExplosion((runcount / 15) % 2 == 0 ? new Color4(94, 0, 57, 255) : new Color4(6, 84, 0, 255), runcount % 6 != 0)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -11,6 +11,7 @@ using osu.Framework.Extensions.Color4Extensions;
|
|||||||
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.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -40,6 +41,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
{
|
{
|
||||||
Child = new FillFlowContainer
|
Child = new FillFlowContainer
|
||||||
{
|
{
|
||||||
|
Clock = new FramedClock(new ManualClock()),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
@ -62,7 +64,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
private Drawable createNoteDisplay(ScrollingDirection direction, int identifier, out DrawableNote hitObject)
|
private Drawable createNoteDisplay(ScrollingDirection direction, int identifier, out DrawableNote hitObject)
|
||||||
{
|
{
|
||||||
var note = new Note { StartTime = 999999999 };
|
var note = new Note { StartTime = 0 };
|
||||||
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
return new ScrollingTestContainer(direction)
|
return new ScrollingTestContainer(direction)
|
||||||
@ -77,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
private Drawable createHoldNoteDisplay(ScrollingDirection direction, int identifier, out DrawableHoldNote hitObject)
|
private Drawable createHoldNoteDisplay(ScrollingDirection direction, int identifier, out DrawableHoldNote hitObject)
|
||||||
{
|
{
|
||||||
var note = new HoldNote { StartTime = 999999999, Duration = 5000 };
|
var note = new HoldNote { StartTime = 0, Duration = 5000 };
|
||||||
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
return new ScrollingTestContainer(direction)
|
return new ScrollingTestContainer(direction)
|
||||||
@ -133,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Width = 1.25f,
|
Width = 1.25f,
|
||||||
Colour = Color4.Black.Opacity(0.5f)
|
Colour = Color4.Green.Opacity(0.5f)
|
||||||
},
|
},
|
||||||
content = new Container { RelativeSizeAxes = Axes.Both }
|
content = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ using osu.Game.Rulesets.Mania.Objects;
|
|||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -114,8 +115,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
var obj = new BarLine
|
var obj = new BarLine
|
||||||
{
|
{
|
||||||
StartTime = Time.Current + 2000,
|
StartTime = Time.Current + 2000,
|
||||||
ControlPoint = new TimingControlPoint(),
|
Major = major,
|
||||||
BeatIndex = major ? 0 : 1
|
|
||||||
};
|
};
|
||||||
|
|
||||||
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
|||||||
{
|
{
|
||||||
base.InitialiseDefaults();
|
base.InitialiseDefaults();
|
||||||
|
|
||||||
Set(ManiaRulesetSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
|
Set(ManiaRulesetSetting.ScrollTime, 1500.0, 50.0, 5000.0, 50.0);
|
||||||
Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
// 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.ControlPoints;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects
|
|
||||||
{
|
|
||||||
public class BarLine : ManiaHitObject
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The control point which this bar line is part of.
|
|
||||||
/// </summary>
|
|
||||||
public TimingControlPoint ControlPoint;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The index of the beat which this bar line represents within the control point.
|
|
||||||
/// This is a "major" bar line if <see cref="BeatIndex"/> % <see cref="TimingControlPoint.TimeSignature"/> == 0.
|
|
||||||
/// </summary>
|
|
||||||
public int BeatIndex;
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,6 +4,7 @@
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
/// Visualises a <see cref="BarLine"/>. Although this derives DrawableManiaHitObject,
|
/// Visualises a <see cref="BarLine"/>. Although this derives DrawableManiaHitObject,
|
||||||
/// this does not handle input/sound like a normal hit object.
|
/// this does not handle input/sound like a normal hit object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DrawableBarLine : DrawableManiaHitObject<BarLine>
|
public class DrawableBarLine : DrawableHitObject<BarLine>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Height of major bar line triangles.
|
/// Height of major bar line triangles.
|
||||||
@ -40,9 +41,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
Colour = new Color4(255, 204, 33, 255),
|
Colour = new Color4(255, 204, 33, 255),
|
||||||
});
|
});
|
||||||
|
|
||||||
bool isMajor = barLine.BeatIndex % (int)barLine.ControlPoint.TimeSignature == 0;
|
if (barLine.Major)
|
||||||
|
|
||||||
if (isMajor)
|
|
||||||
{
|
{
|
||||||
AddInternal(new EquilateralTriangle
|
AddInternal(new EquilateralTriangle
|
||||||
{
|
{
|
||||||
@ -65,10 +64,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMajor && barLine.BeatIndex % 2 == 1)
|
if (!barLine.Major)
|
||||||
Alpha = 0.2f;
|
Alpha = 0.2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void UpdateInitialTransforms()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateStateTransforms(ArmedState state)
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -51,11 +51,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Miss:
|
case ArmedState.Miss:
|
||||||
this.FadeOut(150, Easing.In).Expire();
|
this.FadeOut(150, Easing.In);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
this.FadeOut(150, Easing.OutQuint).Expire();
|
this.FadeOut(150, Easing.OutQuint);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHandler<ManiaAction>
|
public class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHandler<ManiaAction>
|
||||||
{
|
{
|
||||||
|
public const float CORNER_RADIUS = NotePiece.NOTE_HEIGHT / 2;
|
||||||
|
|
||||||
private readonly NotePiece headPiece;
|
private readonly NotePiece headPiece;
|
||||||
|
|
||||||
public DrawableNote(Note hitObject)
|
public DrawableNote(Note hitObject)
|
||||||
@ -38,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Glow,
|
Type = EdgeEffectType.Glow,
|
||||||
Colour = colour.NewValue.Lighten(1f).Opacity(0.6f),
|
Colour = colour.NewValue.Lighten(1f).Opacity(0.2f),
|
||||||
Radius = 10,
|
Radius = 10,
|
||||||
};
|
};
|
||||||
}, true);
|
}, true);
|
||||||
|
@ -18,8 +18,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class NotePiece : Container, IHasAccentColour
|
internal class NotePiece : Container, IHasAccentColour
|
||||||
{
|
{
|
||||||
public const float NOTE_HEIGHT = 10;
|
public const float NOTE_HEIGHT = 12;
|
||||||
private const float head_colour_height = 6;
|
|
||||||
|
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
@ -39,8 +38,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
|||||||
colouredBox = new Box
|
colouredBox = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = head_colour_height,
|
Height = NOTE_HEIGHT / 2,
|
||||||
Alpha = 0.2f
|
Alpha = 0.1f
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -11,6 +11,8 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Rulesets.Mania.UI.Components;
|
using osu.Game.Rulesets.Mania.UI.Components;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -19,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
public class Column : ScrollingPlayfield, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
public class Column : ScrollingPlayfield, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
||||||
{
|
{
|
||||||
private const float column_width = 45;
|
public const float COLUMN_WIDTH = 80;
|
||||||
private const float special_column_width = 70;
|
private const float special_column_width = 70;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -41,10 +43,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Index = index;
|
Index = index;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
Width = column_width;
|
Width = COLUMN_WIDTH;
|
||||||
|
|
||||||
Masking = true;
|
|
||||||
CornerRadius = 5;
|
|
||||||
|
|
||||||
background = new ColumnBackground { RelativeSizeAxes = Axes.Both };
|
background = new ColumnBackground { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
explosionContainer = new Container
|
explosionContainer = new Container
|
||||||
{
|
{
|
||||||
Name = "Hit explosions",
|
Name = "Hit explosions",
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -90,6 +89,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Bottom = dir.NewValue == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0,
|
Bottom = dir.NewValue == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
explosionContainer.Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = dir.NewValue == ScrollingDirection.Up ? NotePiece.NOTE_HEIGHT / 2 : 0,
|
||||||
|
Bottom = dir.NewValue == ScrollingDirection.Down ? NotePiece.NOTE_HEIGHT / 2 : 0
|
||||||
|
};
|
||||||
|
|
||||||
keyArea.Anchor = keyArea.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
keyArea.Anchor = keyArea.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
@ -108,7 +113,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
isSpecial = value;
|
isSpecial = value;
|
||||||
|
|
||||||
Width = isSpecial ? special_column_width : column_width;
|
Width = isSpecial ? special_column_width : COLUMN_WIDTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +168,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value)
|
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
explosionContainer.Add(new HitExplosion(judgedObject)
|
explosionContainer.Add(new HitExplosion(judgedObject.AccentColour.Value, judgedObject is DrawableHoldNoteTick)
|
||||||
{
|
{
|
||||||
Anchor = Direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre
|
Anchor = Direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.Centre
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
{
|
{
|
||||||
Name = "Background",
|
Name = "Background",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0.3f
|
|
||||||
},
|
},
|
||||||
backgroundOverlay = new Box
|
backgroundOverlay = new Box
|
||||||
{
|
{
|
||||||
@ -82,7 +81,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
if (!IsLoaded)
|
if (!IsLoaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
background.Colour = AccentColour;
|
background.Colour = AccentColour.Darken(5);
|
||||||
|
|
||||||
var brightPoint = AccentColour.Opacity(0.6f);
|
var brightPoint = AccentColour.Opacity(0.6f);
|
||||||
var dimPoint = AccentColour.Opacity(0);
|
var dimPoint = AccentColour.Opacity(0);
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -17,7 +18,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
{
|
{
|
||||||
public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour
|
public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour
|
||||||
{
|
{
|
||||||
private const float hit_target_height = 10;
|
|
||||||
private const float hit_target_bar_height = 2;
|
private const float hit_target_bar_height = 2;
|
||||||
|
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
@ -32,7 +32,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
hitTargetBar = new Box
|
hitTargetBar = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = hit_target_height,
|
Height = NotePiece.NOTE_HEIGHT,
|
||||||
|
Alpha = 0.6f,
|
||||||
Colour = Color4.Black
|
Colour = Color4.Black
|
||||||
},
|
},
|
||||||
hitTargetLine = new Container
|
hitTargetLine = new Container
|
||||||
|
@ -2,14 +2,11 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.MathUtils;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
@ -19,8 +16,8 @@ using osu.Game.Rulesets.Mania.Objects.Drawables;
|
|||||||
using osu.Game.Rulesets.Mania.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
using osu.Game.Rulesets.Mania.Scoring;
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
@ -45,33 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
public DrawableManiaRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableManiaRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
// Generate the bar lines
|
BarLines = new BarLineGenerator(Beatmap).BarLines;
|
||||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
|
||||||
|
|
||||||
var timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
|
||||||
var barLines = new List<BarLine>();
|
|
||||||
|
|
||||||
for (int i = 0; i < timingPoints.Count; i++)
|
|
||||||
{
|
|
||||||
TimingControlPoint point = timingPoints[i];
|
|
||||||
|
|
||||||
// Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
|
|
||||||
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
|
|
||||||
{
|
|
||||||
barLines.Add(new BarLine
|
|
||||||
{
|
|
||||||
StartTime = t,
|
|
||||||
ControlPoint = point,
|
|
||||||
BeatIndex = index
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BarLines = barLines;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osuTK.Graphics;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
@ -18,51 +16,112 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
public override bool RemoveWhenNotAlive => true;
|
public override bool RemoveWhenNotAlive => true;
|
||||||
|
|
||||||
private readonly CircularContainer circle;
|
private readonly CircularContainer largeFaint;
|
||||||
|
private readonly CircularContainer mainGlow1;
|
||||||
|
|
||||||
public HitExplosion(DrawableHitObject judgedObject)
|
public HitExplosion(Color4 objectColour, bool isSmall = false)
|
||||||
{
|
{
|
||||||
bool isTick = judgedObject is DrawableHoldNoteTick;
|
|
||||||
|
|
||||||
Origin = Anchor.Centre;
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Y = NotePiece.NOTE_HEIGHT / 2;
|
|
||||||
Height = NotePiece.NOTE_HEIGHT;
|
Height = NotePiece.NOTE_HEIGHT;
|
||||||
|
|
||||||
// scale roughly in-line with visual appearance of notes
|
// scale roughly in-line with visual appearance of notes
|
||||||
Scale = new Vector2(isTick ? 0.4f : 0.8f);
|
Scale = new Vector2(1f, 0.6f);
|
||||||
|
|
||||||
InternalChild = circle = new CircularContainer
|
if (isSmall)
|
||||||
|
Scale *= 0.5f;
|
||||||
|
|
||||||
|
const float angle_variangle = 15; // should be less than 45
|
||||||
|
|
||||||
|
const float roundness = 80;
|
||||||
|
|
||||||
|
const float initial_height = 10;
|
||||||
|
|
||||||
|
var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1);
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
largeFaint = new CircularContainer
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Masking = true,
|
|
||||||
// we want our size to be very small so the glow dominates it.
|
|
||||||
Size = new Vector2(0.1f),
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Glow,
|
Anchor = Anchor.Centre,
|
||||||
Colour = Interpolation.ValueAt(0.1f, judgedObject.AccentColour.Value, Color4.White, 0, 1),
|
Origin = Anchor.Centre,
|
||||||
Radius = 100,
|
|
||||||
},
|
|
||||||
Child = new Box
|
|
||||||
{
|
|
||||||
Alpha = 0,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
AlwaysPresent = true
|
Masking = true,
|
||||||
|
// we want our size to be very small so the glow dominates it.
|
||||||
|
Size = new Vector2(0.8f),
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f),
|
||||||
|
Roundness = 160,
|
||||||
|
Radius = 200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mainGlow1 = new CircularContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1),
|
||||||
|
Roundness = 20,
|
||||||
|
Radius = 50,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new CircularContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Size = new Vector2(0.01f, initial_height),
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Colour = colour,
|
||||||
|
Roundness = roundness,
|
||||||
|
Radius = 40,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new CircularContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Size = new Vector2(0.01f, initial_height),
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
Rotation = RNG.NextSingle(-angle_variangle, angle_variangle),
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Colour = colour,
|
||||||
|
Roundness = roundness,
|
||||||
|
Radius = 40,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
|
const double duration = 200;
|
||||||
|
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
circle.ResizeTo(circle.Size * new Vector2(4, 20), 1000, Easing.OutQuint);
|
largeFaint
|
||||||
this.FadeIn(16).Then().FadeOut(500, Easing.OutQuint);
|
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
||||||
|
.FadeOut(duration * 2);
|
||||||
|
|
||||||
|
mainGlow1.ScaleTo(1.4f, duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
this.FadeOut(duration, Easing.Out);
|
||||||
Expire(true);
|
Expire(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
@ -12,6 +12,7 @@ using osu.Game.Rulesets.Judgements;
|
|||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
@ -26,12 +26,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio, SkinManager skinManager)
|
||||||
{
|
{
|
||||||
var dllStore = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll");
|
var dllStore = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll");
|
||||||
|
|
||||||
metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
|
metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
|
||||||
defaultSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/default_skin"), audio, false);
|
defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
|
||||||
specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/special_skin"), audio, true);
|
specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/special_skin"), audio, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
128
osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// 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.Audio.Sample;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Testing.Input;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneCursorTrail : OsuTestScene
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestSmoothCursorTrail()
|
||||||
|
{
|
||||||
|
Container scalingContainer = null;
|
||||||
|
|
||||||
|
createTest(() => scalingContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new CursorTrail()
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set large scale", () => scalingContainer.Scale = new Vector2(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLegacySmoothCursorTrail()
|
||||||
|
{
|
||||||
|
createTest(() => new LegacySkinContainer(false)
|
||||||
|
{
|
||||||
|
Child = new LegacyCursorTrail()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLegacyDisjointCursorTrail()
|
||||||
|
{
|
||||||
|
createTest(() => new LegacySkinContainer(true)
|
||||||
|
{
|
||||||
|
Child = new LegacyCursorTrail()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTest(Func<Drawable> createContent) => AddStep("create trail", () =>
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
Add(new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Size = new Vector2(0.8f),
|
||||||
|
Child = new MovingCursorInputManager { Child = createContent?.Invoke() }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
[Cached(typeof(ISkinSource))]
|
||||||
|
private class LegacySkinContainer : Container, ISkinSource
|
||||||
|
{
|
||||||
|
private readonly bool disjoint;
|
||||||
|
|
||||||
|
public LegacySkinContainer(bool disjoint)
|
||||||
|
{
|
||||||
|
this.disjoint = disjoint;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public Texture GetTexture(string componentName)
|
||||||
|
{
|
||||||
|
switch (componentName)
|
||||||
|
{
|
||||||
|
case "cursortrail":
|
||||||
|
var tex = new Texture(Texture.WhitePixel.TextureGL);
|
||||||
|
|
||||||
|
if (disjoint)
|
||||||
|
tex.ScaleAdjust = 1 / 25f;
|
||||||
|
return tex;
|
||||||
|
|
||||||
|
case "cursormiddle":
|
||||||
|
return disjoint ? null : Texture.WhitePixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public event Action SourceChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MovingCursorInputManager : ManualInputManager
|
||||||
|
{
|
||||||
|
public MovingCursorInputManager()
|
||||||
|
{
|
||||||
|
UseParentInput = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
const double spin_duration = 1000;
|
||||||
|
double currentTime = Time.Current;
|
||||||
|
|
||||||
|
double angle = (currentTime % spin_duration) / spin_duration * 2 * Math.PI;
|
||||||
|
Vector2 rPos = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
|
||||||
|
|
||||||
|
MoveMouseTo(ToScreenSpace(DrawSize / 2 + DrawSize / 3 * rPos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,23 +6,68 @@ using System.Collections.Generic;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing.Input;
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneGameplayCursor : SkinnableTestScene
|
public class TestSceneGameplayCursor : SkinnableTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(CursorTrail) };
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(OsuCursorContainer),
|
||||||
|
typeof(CursorTrail)
|
||||||
|
};
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new OsuCursorContainer
|
SetContents(() => new MovingCursorInputManager
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
Child = new ClickingCursorContainer
|
||||||
Masking = true,
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ClickingCursorContainer : OsuCursorContainer
|
||||||
|
{
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
double currentTime = Time.Current;
|
||||||
|
|
||||||
|
if (((int)(currentTime / 1000)) % 2 == 0)
|
||||||
|
OnPressed(OsuAction.LeftButton);
|
||||||
|
else
|
||||||
|
OnReleased(OsuAction.LeftButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MovingCursorInputManager : ManualInputManager
|
||||||
|
{
|
||||||
|
public MovingCursorInputManager()
|
||||||
|
{
|
||||||
|
UseParentInput = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
const double spin_duration = 5000;
|
||||||
|
double currentTime = Time.Current;
|
||||||
|
|
||||||
|
double angle = (currentTime % spin_duration) / spin_duration * 2 * Math.PI;
|
||||||
|
Vector2 rPos = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
|
||||||
|
|
||||||
|
MoveMouseTo(ToScreenSpace(DrawSize / 2 + DrawSize / 3 * rPos));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue, true);
|
AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override double LifetimeStart
|
||||||
|
{
|
||||||
|
get => base.LifetimeStart;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.LifetimeStart = value;
|
||||||
|
ApproachCircle.LifetimeStart = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override double LifetimeEnd
|
||||||
|
{
|
||||||
|
get => base.LifetimeEnd;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.LifetimeEnd = value;
|
||||||
|
ApproachCircle.LifetimeEnd = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
Debug.Assert(HitObject.HitWindows != null);
|
Debug.Assert(HitObject.HitWindows != null);
|
||||||
@ -132,22 +152,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
Expire(true);
|
Expire(true);
|
||||||
|
|
||||||
hitArea.HitAction = null;
|
hitArea.HitAction = null;
|
||||||
|
|
||||||
// override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early.
|
|
||||||
LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.WindowFor(HitResult.Miss);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArmedState.Miss:
|
case ArmedState.Miss:
|
||||||
ApproachCircle.FadeOut(50);
|
ApproachCircle.FadeOut(50);
|
||||||
this.FadeOut(100);
|
this.FadeOut(100);
|
||||||
Expire();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
ApproachCircle.FadeOut(50);
|
ApproachCircle.FadeOut(50);
|
||||||
|
|
||||||
// todo: temporary / arbitrary
|
// todo: temporary / arbitrary
|
||||||
this.Delay(800).Expire();
|
this.Delay(800).FadeOut();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
|
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
// Manually set to reduce the number of future alive objects to a bare minimum.
|
||||||
|
LifetimeStart = HitObject.StartTime - HitObject.TimePreempt;
|
||||||
|
}
|
||||||
|
|
||||||
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
|
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,10 +219,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.FadeOut(fade_out_time, Easing.OutQuint).Expire();
|
this.FadeOut(fade_out_time, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
Expire(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
|
public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
|
||||||
|
@ -219,10 +219,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
|
||||||
Expire(true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out);
|
sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out);
|
||||||
break;
|
break;
|
||||||
@ -231,8 +227,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
sequence.ScaleTo(Scale * 0.8f, 320, Easing.In);
|
sequence.ScaleTo(Scale * 0.8f, 320, Easing.In);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expire();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
HitCircle,
|
HitCircle,
|
||||||
FollowPoint,
|
FollowPoint,
|
||||||
Cursor,
|
Cursor,
|
||||||
|
CursorTrail,
|
||||||
SliderScorePoint,
|
SliderScorePoint,
|
||||||
ApproachCircle,
|
ApproachCircle,
|
||||||
ReverseArrow,
|
ReverseArrow,
|
||||||
|
55
osu.Game.Rulesets.Osu/Skinning/LegacyCursorTrail.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Skinning
|
||||||
|
{
|
||||||
|
public class LegacyCursorTrail : CursorTrail
|
||||||
|
{
|
||||||
|
private const double disjoint_trail_time_separation = 1000 / 60.0;
|
||||||
|
|
||||||
|
private bool disjointTrail;
|
||||||
|
private double lastTrailTime;
|
||||||
|
|
||||||
|
public LegacyCursorTrail()
|
||||||
|
{
|
||||||
|
Blending = BlendingParameters.Additive;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
Texture = skin.GetTexture("cursortrail");
|
||||||
|
disjointTrail = skin.GetTexture("cursormiddle") == null;
|
||||||
|
|
||||||
|
if (Texture != null)
|
||||||
|
{
|
||||||
|
// stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation.
|
||||||
|
Texture.ScaleAdjust *= 1.6f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double FadeDuration => disjointTrail ? 150 : 500;
|
||||||
|
|
||||||
|
protected override bool InterpolateMovements => !disjointTrail;
|
||||||
|
|
||||||
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
|
{
|
||||||
|
if (!disjointTrail)
|
||||||
|
return base.OnMouseMove(e);
|
||||||
|
|
||||||
|
if (Time.Current - lastTrailTime >= disjoint_trail_time_separation)
|
||||||
|
{
|
||||||
|
lastTrailTime = Time.Current;
|
||||||
|
return base.OnMouseMove(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,6 +45,9 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
switch (osuComponent.Component)
|
switch (osuComponent.Component)
|
||||||
{
|
{
|
||||||
|
case OsuSkinComponents.FollowPoint:
|
||||||
|
return this.GetAnimation(component.LookupName, true, false);
|
||||||
|
|
||||||
case OsuSkinComponents.SliderFollowCircle:
|
case OsuSkinComponents.SliderFollowCircle:
|
||||||
return this.GetAnimation("sliderfollowcircle", true, true);
|
return this.GetAnimation("sliderfollowcircle", true, true);
|
||||||
|
|
||||||
@ -78,17 +81,23 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.CursorTrail:
|
||||||
|
if (source.GetTexture("cursortrail") != null)
|
||||||
|
return new LegacyCursorTrail();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
case OsuSkinComponents.HitCircleText:
|
case OsuSkinComponents.HitCircleText:
|
||||||
var font = GetConfig<OsuSkinConfiguration, string>(OsuSkinConfiguration.HitCircleFont)?.Value ?? "default";
|
var font = GetConfig<OsuSkinConfiguration, string>(OsuSkinConfiguration.HitCirclePrefix)?.Value ?? "default";
|
||||||
var overlap = GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0;
|
var overlap = GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.HitCircleOverlap)?.Value ?? 0;
|
||||||
|
|
||||||
return !hasFont(font)
|
return !hasFont(font)
|
||||||
? null
|
? null
|
||||||
: new LegacySpriteText(source, font)
|
: new LegacySpriteText(source, font)
|
||||||
{
|
{
|
||||||
// Spacing value was reverse-engineered from the ratio of the rendered sprite size in the visual inspector vs the actual texture size
|
// stable applies a blanket 0.8x scale to hitcircle fonts
|
||||||
Scale = new Vector2(0.96f),
|
Scale = new Vector2(0.8f),
|
||||||
Spacing = new Vector2(-overlap * 0.89f, 0)
|
Spacing = new Vector2(-overlap, 0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
public enum OsuSkinConfiguration
|
public enum OsuSkinConfiguration
|
||||||
{
|
{
|
||||||
HitCircleFont,
|
HitCirclePrefix,
|
||||||
HitCircleOverlap,
|
HitCircleOverlap,
|
||||||
SliderBorderSize,
|
SliderBorderSize,
|
||||||
SliderPathRadius,
|
SliderPathRadius,
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Caching;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Batches;
|
using osu.Framework.Graphics.Batches;
|
||||||
using osu.Framework.Graphics.OpenGL.Vertices;
|
using osu.Framework.Graphics.OpenGL.Vertices;
|
||||||
@ -20,30 +21,15 @@ using osuTK.Graphics.ES30;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||||
{
|
{
|
||||||
internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition
|
public class CursorTrail : Drawable, IRequireHighFrequencyMousePosition
|
||||||
{
|
{
|
||||||
private int currentIndex;
|
|
||||||
|
|
||||||
private IShader shader;
|
|
||||||
private Texture texture;
|
|
||||||
|
|
||||||
private Vector2 size => texture.Size * Scale;
|
|
||||||
|
|
||||||
private double timeOffset;
|
|
||||||
|
|
||||||
private float time;
|
|
||||||
|
|
||||||
public override bool IsPresent => true;
|
|
||||||
|
|
||||||
private const int max_sprites = 2048;
|
private const int max_sprites = 2048;
|
||||||
|
|
||||||
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
||||||
|
private int currentIndex;
|
||||||
private Vector2? lastPosition;
|
private IShader shader;
|
||||||
|
private double timeOffset;
|
||||||
private readonly InputResampler resampler = new InputResampler();
|
private float time;
|
||||||
|
|
||||||
protected override DrawNode CreateDrawNode() => new TrailDrawNode(this);
|
|
||||||
|
|
||||||
public CursorTrail()
|
public CursorTrail()
|
||||||
{
|
{
|
||||||
@ -60,14 +46,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ShaderManager shaders, TextureStore textures)
|
private void load(ShaderManager shaders)
|
||||||
{
|
{
|
||||||
shader = shaders.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE);
|
shader = shaders.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE);
|
||||||
texture = textures.Get(@"Cursor/cursortrail");
|
|
||||||
Scale = new Vector2(1 / texture.ScaleAdjust);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -76,6 +58,42 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
resetTime();
|
resetTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Texture texture = Texture.WhitePixel;
|
||||||
|
|
||||||
|
public Texture Texture
|
||||||
|
{
|
||||||
|
get => texture;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (texture == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
texture = value;
|
||||||
|
Invalidate(Invalidation.DrawNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Cached<Vector2> partSizeCache = new Cached<Vector2>();
|
||||||
|
|
||||||
|
private Vector2 partSize => partSizeCache.IsValid
|
||||||
|
? partSizeCache.Value
|
||||||
|
: (partSizeCache.Value = new Vector2(Texture.DisplayWidth, Texture.DisplayHeight) * DrawInfo.Matrix.ExtractScale().Xy);
|
||||||
|
|
||||||
|
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
||||||
|
{
|
||||||
|
if ((invalidation & (Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit | Invalidation.Presence)) > 0)
|
||||||
|
partSizeCache.Invalidate();
|
||||||
|
|
||||||
|
return base.Invalidate(invalidation, source, shallPropagate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time to fade the cursor trail pieces.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual double FadeDuration => 300;
|
||||||
|
|
||||||
|
public override bool IsPresent => true;
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -84,7 +102,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) / 300f;
|
time = (float)((Time.Current - timeOffset) / FadeDuration);
|
||||||
if (time > fade_clock_reset_threshold)
|
if (time > fade_clock_reset_threshold)
|
||||||
resetTime();
|
resetTime();
|
||||||
}
|
}
|
||||||
@ -101,6 +119,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
timeOffset = Time.Current;
|
timeOffset = Time.Current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to interpolate mouse movements and add trail pieces at intermediate points.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual bool InterpolateMovements => true;
|
||||||
|
|
||||||
|
private Vector2? lastPosition;
|
||||||
|
private readonly InputResampler resampler = new InputResampler();
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
Vector2 pos = e.ScreenSpaceMousePosition;
|
Vector2 pos = e.ScreenSpaceMousePosition;
|
||||||
@ -116,33 +144,43 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
{
|
{
|
||||||
Trace.Assert(lastPosition.HasValue);
|
Trace.Assert(lastPosition.HasValue);
|
||||||
|
|
||||||
// ReSharper disable once PossibleInvalidOperationException
|
if (InterpolateMovements)
|
||||||
Vector2 pos1 = lastPosition.Value;
|
|
||||||
Vector2 diff = pos2 - pos1;
|
|
||||||
float distance = diff.Length;
|
|
||||||
Vector2 direction = diff / distance;
|
|
||||||
|
|
||||||
float interval = size.X / 2 * 0.9f;
|
|
||||||
|
|
||||||
for (float d = interval; d < distance; d += interval)
|
|
||||||
{
|
{
|
||||||
lastPosition = pos1 + direction * d;
|
// ReSharper disable once PossibleInvalidOperationException
|
||||||
addPosition(lastPosition.Value);
|
Vector2 pos1 = lastPosition.Value;
|
||||||
|
Vector2 diff = pos2 - pos1;
|
||||||
|
float distance = diff.Length;
|
||||||
|
Vector2 direction = diff / distance;
|
||||||
|
|
||||||
|
float interval = partSize.X / 2.5f;
|
||||||
|
|
||||||
|
for (float d = interval; d < distance; d += interval)
|
||||||
|
{
|
||||||
|
lastPosition = pos1 + direction * d;
|
||||||
|
addPart(lastPosition.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastPosition = pos2;
|
||||||
|
addPart(lastPosition.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnMouseMove(e);
|
return base.OnMouseMove(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPosition(Vector2 pos)
|
private void addPart(Vector2 screenSpacePosition)
|
||||||
{
|
{
|
||||||
parts[currentIndex].Position = pos;
|
parts[currentIndex].Position = screenSpacePosition;
|
||||||
parts[currentIndex].Time = time;
|
parts[currentIndex].Time = time;
|
||||||
++parts[currentIndex].InvalidationID;
|
++parts[currentIndex].InvalidationID;
|
||||||
|
|
||||||
currentIndex = (currentIndex + 1) % max_sprites;
|
currentIndex = (currentIndex + 1) % max_sprites;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override DrawNode CreateDrawNode() => new TrailDrawNode(this);
|
||||||
|
|
||||||
private struct TrailPart
|
private struct TrailPart
|
||||||
{
|
{
|
||||||
public Vector2 Position;
|
public Vector2 Position;
|
||||||
@ -177,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
shader = Source.shader;
|
shader = Source.shader;
|
||||||
texture = Source.texture;
|
texture = Source.texture;
|
||||||
size = Source.size;
|
size = Source.partSize;
|
||||||
time = Source.time;
|
time = Source.time;
|
||||||
|
|
||||||
for (int i = 0; i < Source.parts.Length; ++i)
|
for (int i = 0; i < Source.parts.Length; ++i)
|
||||||
|
@ -6,9 +6,12 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Osu.Configuration;
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||||
{
|
{
|
||||||
@ -22,17 +25,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
private readonly Bindable<bool> showTrail = new Bindable<bool>(true);
|
private readonly Bindable<bool> showTrail = new Bindable<bool>(true);
|
||||||
|
|
||||||
private readonly CursorTrail cursorTrail;
|
private readonly Drawable cursorTrail;
|
||||||
|
|
||||||
public OsuCursorContainer()
|
public OsuCursorContainer()
|
||||||
{
|
{
|
||||||
InternalChild = fadeContainer = new Container
|
InternalChild = fadeContainer = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Child = cursorTrail = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling)
|
||||||
{
|
|
||||||
cursorTrail = new CursorTrail { Depth = 1 }
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,5 +98,15 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
|
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
|
||||||
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
|
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DefaultCursorTrail : CursorTrail
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(TextureStore textures)
|
||||||
|
{
|
||||||
|
Texture = textures.Get(@"Cursor/cursortrail");
|
||||||
|
Scale = new Vector2(1 / Texture.ScaleAdjust);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,13 +70,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
base.Add(h);
|
base.Add(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addApproachCircleProxy(Drawable d)
|
private void addApproachCircleProxy(Drawable d) => approachCircles.Add(d.CreateProxy());
|
||||||
{
|
|
||||||
var proxy = d.CreateProxy();
|
|
||||||
proxy.LifetimeStart = d.LifetimeStart;
|
|
||||||
proxy.LifetimeEnd = d.LifetimeEnd;
|
|
||||||
approachCircles.Add(proxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void PostProcess()
|
public override void PostProcess()
|
||||||
{
|
{
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects
|
|
||||||
{
|
|
||||||
public class BarLine : TaikoHitObject
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A line that scrolls alongside hit objects in the playfield and visualises control points.
|
/// A line that scrolls alongside hit objects in the playfield and visualises control points.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DrawableBarLine : DrawableHitObject<TaikoHitObject>
|
public class DrawableBarLine : DrawableHitObject<HitObject>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The width of the line tracker.
|
/// The width of the line tracker.
|
||||||
|
@ -5,6 +5,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||||
{
|
{
|
||||||
|
@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
case ArmedState.Miss:
|
case ArmedState.Miss:
|
||||||
this.Delay(HitObject.Duration).FadeOut(100).Expire();
|
this.Delay(HitObject.Duration).FadeOut(100);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
this.ScaleTo(0, 100, Easing.OutQuint).Expire();
|
this.ScaleTo(0, 100, Easing.OutQuint);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,12 +105,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
validActionPressed = false;
|
validActionPressed = false;
|
||||||
|
|
||||||
UnproxyContent();
|
UnproxyContent();
|
||||||
this.Delay(HitObject.HitWindows.WindowFor(HitResult.Miss)).Expire();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArmedState.Miss:
|
case ArmedState.Miss:
|
||||||
this.FadeOut(100)
|
this.FadeOut(100);
|
||||||
.Expire();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
@ -129,9 +127,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
.Then()
|
.Then()
|
||||||
.MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In);
|
.MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In);
|
||||||
|
|
||||||
this.FadeOut(800)
|
this.FadeOut(800);
|
||||||
.Expire();
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,8 +208,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
this.FadeOut(transition_duration, Easing.Out);
|
this.FadeOut(transition_duration, Easing.Out);
|
||||||
bodyContainer.ScaleTo(1.4f, transition_duration);
|
bodyContainer.ScaleTo(1.4f, transition_duration);
|
||||||
|
|
||||||
Expire();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -5,19 +5,18 @@ using System.Collections.Generic;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.Scoring;
|
using osu.Game.Rulesets.Taiko.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.Taiko.Replays;
|
using osu.Game.Rulesets.Taiko.Replays;
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.UI
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
@ -38,49 +37,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
loadBarLines();
|
new BarLineGenerator(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
|
||||||
}
|
|
||||||
|
|
||||||
private void loadBarLines()
|
|
||||||
{
|
|
||||||
TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1];
|
|
||||||
double lastHitTime = 1 + ((lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime);
|
|
||||||
|
|
||||||
var timingPoints = Beatmap.ControlPointInfo.TimingPoints.ToList();
|
|
||||||
|
|
||||||
if (timingPoints.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int currentIndex = 0;
|
|
||||||
int currentBeat = 0;
|
|
||||||
double time = timingPoints[currentIndex].Time;
|
|
||||||
|
|
||||||
while (time <= lastHitTime)
|
|
||||||
{
|
|
||||||
int nextIndex = currentIndex + 1;
|
|
||||||
|
|
||||||
if (nextIndex < timingPoints.Count && time > timingPoints[nextIndex].Time)
|
|
||||||
{
|
|
||||||
currentIndex = nextIndex;
|
|
||||||
time = timingPoints[currentIndex].Time;
|
|
||||||
currentBeat = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentPoint = timingPoints[currentIndex];
|
|
||||||
|
|
||||||
var barLine = new BarLine
|
|
||||||
{
|
|
||||||
StartTime = time,
|
|
||||||
};
|
|
||||||
|
|
||||||
barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
|
|
||||||
|
|
||||||
bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0;
|
|
||||||
Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
|
|
||||||
|
|
||||||
time += currentPoint.BeatLength * (int)currentPoint.TimeSignature;
|
|
||||||
currentBeat++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
|
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
|
||||||
|
108
osu.Game.Tests/Visual/Online/TestSceneChatLineTruncation.cs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Online.Chat;
|
||||||
|
using osu.Game.Overlays.Chat;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneChatLineTruncation : OsuTestScene
|
||||||
|
{
|
||||||
|
private readonly TestChatLineContainer textContainer;
|
||||||
|
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(ChatLine),
|
||||||
|
typeof(Message),
|
||||||
|
typeof(LinkFlowContainer),
|
||||||
|
typeof(MessageFormatter)
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestSceneChatLineTruncation()
|
||||||
|
{
|
||||||
|
Add(textContainer = new TestChatLineContainer
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding { Left = 20, Right = 20 },
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
testFormatting();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clear() => AddStep("clear messages", textContainer.Clear);
|
||||||
|
|
||||||
|
private void addMessageWithChecks(string text, bool isAction = false, bool isImportant = false, string username = null)
|
||||||
|
{
|
||||||
|
int index = textContainer.Count + 1;
|
||||||
|
var newLine = new ChatLine(new DummyMessage(text, isAction, isImportant, index, username));
|
||||||
|
textContainer.Add(newLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testFormatting()
|
||||||
|
{
|
||||||
|
for (int a = 0; a < 25; a++)
|
||||||
|
addMessageWithChecks($"Wide {a} character username.", username: new string('w', a));
|
||||||
|
addMessageWithChecks("Short name with spaces.", username: "sho rt name");
|
||||||
|
addMessageWithChecks("Long name with spaces.", username: "long name with s p a c e s");
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DummyMessage : Message
|
||||||
|
{
|
||||||
|
private static long messageCounter;
|
||||||
|
|
||||||
|
internal static readonly User TEST_SENDER_BACKGROUND = new User
|
||||||
|
{
|
||||||
|
Username = @"i-am-important",
|
||||||
|
Id = 42,
|
||||||
|
Colour = "#250cc9",
|
||||||
|
};
|
||||||
|
|
||||||
|
internal static readonly User TEST_SENDER = new User
|
||||||
|
{
|
||||||
|
Username = @"Somebody",
|
||||||
|
Id = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
public new DateTimeOffset Timestamp = DateTimeOffset.Now;
|
||||||
|
|
||||||
|
public DummyMessage(string text, bool isAction = false, bool isImportant = false, int number = 0, string username = null)
|
||||||
|
: base(messageCounter++)
|
||||||
|
{
|
||||||
|
Content = text;
|
||||||
|
IsAction = isAction;
|
||||||
|
Sender = new User
|
||||||
|
{
|
||||||
|
Username = username ?? $"user {number}",
|
||||||
|
Id = number,
|
||||||
|
Colour = isImportant ? "#250cc9" : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestChatLineContainer : FillFlowContainer<ChatLine>
|
||||||
|
{
|
||||||
|
protected override int Compare(Drawable x, Drawable y)
|
||||||
|
{
|
||||||
|
var xC = (ChatLine)x;
|
||||||
|
var yC = (ChatLine)y;
|
||||||
|
|
||||||
|
return xC.Message.CompareTo(yC.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using osu.Game.Overlays.Rankings;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
|
using osu.Game.Rulesets.Taiko;
|
||||||
|
using osu.Game.Rulesets.Catch;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneRankingsRulesetSelector : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(RankingsRulesetSelector),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestSceneRankingsRulesetSelector()
|
||||||
|
{
|
||||||
|
var current = new Bindable<RulesetInfo>();
|
||||||
|
|
||||||
|
Add(new RankingsRulesetSelector
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Current = { BindTarget = current }
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("Select osu!", () => current.Value = new OsuRuleset().RulesetInfo);
|
||||||
|
AddStep("Select mania", () => current.Value = new ManiaRuleset().RulesetInfo);
|
||||||
|
AddStep("Select taiko", () => current.Value = new TaikoRuleset().RulesetInfo);
|
||||||
|
AddStep("Select catch", () => current.Value = new CatchRuleset().RulesetInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.SongSelect
|
namespace osu.Game.Tests.Visual.SongSelect
|
||||||
@ -30,45 +31,44 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Size = new Vector2(550f, 450f),
|
Size = new Vector2(550f, 450f),
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
AddStep("all metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
BeatmapSetInfo =
|
BeatmapSet = new BeatmapSetInfo
|
||||||
{
|
{
|
||||||
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
||||||
},
|
},
|
||||||
BeatmapInfo =
|
Version = "All Metrics",
|
||||||
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
Version = "All Metrics",
|
Source = "osu!lazer",
|
||||||
Metadata = new BeatmapMetadata
|
Tags = "this beatmap has all the metrics",
|
||||||
{
|
},
|
||||||
Source = "osu!lazer",
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
Tags = "this beatmap has all the metrics",
|
{
|
||||||
},
|
CircleSize = 7,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
DrainRate = 1,
|
||||||
{
|
OverallDifficulty = 5.7f,
|
||||||
CircleSize = 7,
|
ApproachRate = 3.5f,
|
||||||
DrainRate = 1,
|
},
|
||||||
OverallDifficulty = 5.7f,
|
StarDifficulty = 5.3f,
|
||||||
ApproachRate = 3.5f,
|
Metrics = new BeatmapMetrics
|
||||||
},
|
{
|
||||||
StarDifficulty = 5.3f,
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Metrics = new BeatmapMetrics
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
{
|
},
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
}));
|
||||||
|
|
||||||
AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
AddStep("all except source", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
BeatmapSetInfo =
|
|
||||||
{
|
|
||||||
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
|
||||||
},
|
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
|
BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
||||||
|
},
|
||||||
Version = "All Metrics",
|
Version = "All Metrics",
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
@ -88,16 +88,16 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
AddStep("ratings", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
BeatmapSetInfo =
|
|
||||||
{
|
|
||||||
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
|
||||||
},
|
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
|
BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
||||||
|
},
|
||||||
Version = "Only Ratings",
|
Version = "Only Ratings",
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
@ -113,9 +113,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
},
|
},
|
||||||
StarDifficulty = 4.8f
|
StarDifficulty = 4.8f
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
AddStep("fails+retries", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
AddStep("fails+retries", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
@ -139,9 +139,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
AddStep("null metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
AddStep("null metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
@ -160,7 +160,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
},
|
},
|
||||||
StarDifficulty = 1.97f,
|
StarDifficulty = 1.97f,
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
AddStep("null beatmap", () => detailsArea.Beatmap = null);
|
AddStep("null beatmap", () => detailsArea.Beatmap = null);
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
|
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
|
||||||
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
|
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
|
||||||
AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable));
|
AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable));
|
||||||
|
AddStep(@"None selected", () => leaderboard.SetRetrievalState(PlaceholderState.NoneSelected));
|
||||||
foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus)))
|
foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus)))
|
||||||
AddStep($"{status} beatmap", () => showBeatmapWithStatus(status));
|
AddStep($"{status} beatmap", () => showBeatmapWithStatus(status));
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
protected virtual bool DimMainContent => true;
|
protected virtual bool DimMainContent => true;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private OsuGame osuGame { get; set; }
|
private OsuGame game { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private PreviewTrackManager previewTrackManager { get; set; }
|
private PreviewTrackManager previewTrackManager { get; set; }
|
||||||
@ -42,13 +42,22 @@ namespace osu.Game.Graphics.Containers
|
|||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
if (osuGame != null)
|
|
||||||
OverlayActivationMode.BindTo(osuGame.OverlayActivationMode);
|
|
||||||
|
|
||||||
samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in");
|
samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in");
|
||||||
samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out");
|
samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out");
|
||||||
|
}
|
||||||
|
|
||||||
State.ValueChanged += onStateChanged;
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
if (game != null)
|
||||||
|
OverlayActivationMode.BindTo(game.OverlayActivationMode);
|
||||||
|
|
||||||
|
OverlayActivationMode.BindValueChanged(mode =>
|
||||||
|
{
|
||||||
|
if (mode.NewValue == OverlayActivation.Disabled)
|
||||||
|
State.Value = Visibility.Hidden;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
base.LoadComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -106,26 +115,28 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
public bool OnReleased(GlobalAction action) => false;
|
public bool OnReleased(GlobalAction action) => false;
|
||||||
|
|
||||||
private void onStateChanged(ValueChangedEvent<Visibility> state)
|
protected override void UpdateState(ValueChangedEvent<Visibility> state)
|
||||||
{
|
{
|
||||||
switch (state.NewValue)
|
switch (state.NewValue)
|
||||||
{
|
{
|
||||||
case Visibility.Visible:
|
case Visibility.Visible:
|
||||||
if (OverlayActivationMode.Value != OverlayActivation.Disabled)
|
if (OverlayActivationMode.Value == OverlayActivation.Disabled)
|
||||||
{
|
{
|
||||||
if (PlaySamplesOnStateChange) samplePopIn?.Play();
|
State.Value = Visibility.Hidden;
|
||||||
if (BlockScreenWideMouse && DimMainContent) osuGame?.AddBlockingOverlay(this);
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
Hide();
|
|
||||||
|
|
||||||
|
if (PlaySamplesOnStateChange) samplePopIn?.Play();
|
||||||
|
if (BlockScreenWideMouse && DimMainContent) game?.AddBlockingOverlay(this);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Visibility.Hidden:
|
case Visibility.Hidden:
|
||||||
if (PlaySamplesOnStateChange) samplePopOut?.Play();
|
if (PlaySamplesOnStateChange) samplePopOut?.Play();
|
||||||
if (BlockScreenWideMouse) osuGame?.RemoveBlockingOverlay(this);
|
if (BlockScreenWideMouse) game?.RemoveBlockingOverlay(this);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base.UpdateState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
@ -137,7 +148,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
osuGame?.RemoveBlockingOverlay(this);
|
game?.RemoveBlockingOverlay(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Margin = new MarginPadding { Top = 8, Bottom = 8 },
|
Margin = new MarginPadding { Top = 8, Bottom = 8 },
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Text = (value as Enum)?.GetDescription() ?? value.ToString(),
|
Text = CreateText(),
|
||||||
Font = OsuFont.GetFont(size: 14)
|
Font = OsuFont.GetFont(size: 14)
|
||||||
},
|
},
|
||||||
box = new Box
|
box = new Box
|
||||||
@ -81,6 +81,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
|
Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Exo, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual string CreateText() => (Value as Enum)?.GetDescription() ?? Value.ToString();
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
if (!Active.Value)
|
if (!Active.Value)
|
||||||
|
@ -133,6 +133,10 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PlaceholderState.NoneSelected:
|
||||||
|
replacePlaceholder(new MessagePlaceholder(@"Please select a beatmap!"));
|
||||||
|
break;
|
||||||
|
|
||||||
case PlaceholderState.Unavailable:
|
case PlaceholderState.Unavailable:
|
||||||
replacePlaceholder(new MessagePlaceholder(@"Leaderboards are not available for this beatmap!"));
|
replacePlaceholder(new MessagePlaceholder(@"Leaderboards are not available for this beatmap!"));
|
||||||
break;
|
break;
|
||||||
|
@ -9,6 +9,7 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
Retrieving,
|
Retrieving,
|
||||||
NetworkFailure,
|
NetworkFailure,
|
||||||
Unavailable,
|
Unavailable,
|
||||||
|
NoneSelected,
|
||||||
NoScores,
|
NoScores,
|
||||||
NotLoggedIn,
|
NotLoggedIn,
|
||||||
NotSupporter,
|
NotSupporter,
|
||||||
|
@ -31,6 +31,8 @@ namespace osu.Game.Overlays.Chat
|
|||||||
|
|
||||||
protected virtual float MessagePadding => default_message_padding;
|
protected virtual float MessagePadding => default_message_padding;
|
||||||
|
|
||||||
|
private const float timestamp_padding = 65;
|
||||||
|
|
||||||
private const float default_horizontal_padding = 15;
|
private const float default_horizontal_padding = 15;
|
||||||
|
|
||||||
protected virtual float HorizontalPadding => default_horizontal_padding;
|
protected virtual float HorizontalPadding => default_horizontal_padding;
|
||||||
@ -87,7 +89,12 @@ namespace osu.Game.Overlays.Chat
|
|||||||
{
|
{
|
||||||
Shadow = false,
|
Shadow = false,
|
||||||
Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length],
|
Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length],
|
||||||
Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true)
|
Truncate = true,
|
||||||
|
EllipsisString = "… :",
|
||||||
|
Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Bold, italics: true),
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
MaxWidth = default_message_padding - timestamp_padding
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hasBackground)
|
if (hasBackground)
|
||||||
@ -142,6 +149,7 @@ namespace osu.Game.Overlays.Chat
|
|||||||
new MessageSender(message.Sender)
|
new MessageSender(message.Sender)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Left = timestamp_padding },
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Child = effectedUsername,
|
Child = effectedUsername,
|
||||||
|
56
osu.Game/Overlays/Rankings/RankingsRulesetSelector.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osuTK;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Rankings
|
||||||
|
{
|
||||||
|
public class RankingsRulesetSelector : PageTabControl<RulesetInfo>
|
||||||
|
{
|
||||||
|
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new RankingsTabItem(value);
|
||||||
|
|
||||||
|
protected override Dropdown<RulesetInfo> CreateDropdown() => null;
|
||||||
|
|
||||||
|
public RankingsRulesetSelector()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours, RulesetStore rulesets)
|
||||||
|
{
|
||||||
|
foreach (var r in rulesets.AvailableRulesets)
|
||||||
|
AddItem(r);
|
||||||
|
|
||||||
|
AccentColour = colours.Lime;
|
||||||
|
|
||||||
|
SelectTab(TabContainer.FirstOrDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(20, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
private class RankingsTabItem : PageTabItem
|
||||||
|
{
|
||||||
|
public RankingsTabItem(RulesetInfo value)
|
||||||
|
: base(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string CreateText() => $"{Value.Name}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
osu.Game/Rulesets/Objects/BarLine.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Objects
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A hit object representing the end of a bar.
|
||||||
|
/// </summary>
|
||||||
|
public class BarLine : HitObject
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this barline is a prominent beat (based on time signature of beatmap).
|
||||||
|
/// </summary>
|
||||||
|
public bool Major;
|
||||||
|
}
|
||||||
|
}
|
58
osu.Game/Rulesets/Objects/BarLineGenerator.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Objects
|
||||||
|
{
|
||||||
|
public class BarLineGenerator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The generated bar lines.
|
||||||
|
/// </summary>
|
||||||
|
public readonly List<BarLine> BarLines = new List<BarLine>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs and generates bar lines for provided beatmap.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The beatmap to generate bar lines for.</param>
|
||||||
|
public BarLineGenerator(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
if (beatmap.HitObjects.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
HitObject lastObject = beatmap.HitObjects.Last();
|
||||||
|
double lastHitTime = 1 + ((lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime);
|
||||||
|
|
||||||
|
var timingPoints = beatmap.ControlPointInfo.TimingPoints;
|
||||||
|
|
||||||
|
if (timingPoints.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < timingPoints.Count; i++)
|
||||||
|
{
|
||||||
|
TimingControlPoint currentTimingPoint = timingPoints[i];
|
||||||
|
int currentBeat = 0;
|
||||||
|
|
||||||
|
// Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
|
||||||
|
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - currentTimingPoint.BeatLength : lastHitTime + currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature;
|
||||||
|
|
||||||
|
double barLength = currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature;
|
||||||
|
|
||||||
|
for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++)
|
||||||
|
{
|
||||||
|
BarLines.Add(new BarLine
|
||||||
|
{
|
||||||
|
StartTime = t,
|
||||||
|
Major = currentBeat % (int)currentTimingPoint.TimeSignature == 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -153,7 +153,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
|
|
||||||
if (UseTransformStateManagement)
|
if (UseTransformStateManagement)
|
||||||
{
|
{
|
||||||
lifetimeStart = null;
|
|
||||||
LifetimeEnd = double.MaxValue;
|
LifetimeEnd = double.MaxValue;
|
||||||
|
|
||||||
double transformTime = HitObject.StartTime - InitialLifetimeOffset;
|
double transformTime = HitObject.StartTime - InitialLifetimeOffset;
|
||||||
@ -173,6 +172,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
state.Value = newState;
|
state.Value = newState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.Value != ArmedState.Idle && LifetimeEnd == double.MaxValue)
|
||||||
|
Expire();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
state.Value = newState;
|
state.Value = newState;
|
||||||
@ -203,6 +205,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Apply transforms based on the current <see cref="ArmedState"/>. Previous states are automatically cleared.
|
/// Apply transforms based on the current <see cref="ArmedState"/>. Previous states are automatically cleared.
|
||||||
|
/// In the case of a non-idle <see cref="ArmedState"/>, and if <see cref="Drawable.LifetimeEnd"/> was not set during this call, <see cref="Drawable.Expire"/> will be invoked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The new armed state.</param>
|
/// <param name="state">The new armed state.</param>
|
||||||
protected virtual void UpdateStateTransforms(ArmedState state)
|
protected virtual void UpdateStateTransforms(ArmedState state)
|
||||||
@ -311,7 +314,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is only used as an optimisation to delay the initial update of this <see cref="DrawableHitObject"/> and may be tuned more aggressively if required.
|
/// This is only used as an optimisation to delay the initial update of this <see cref="DrawableHitObject"/> and may be tuned more aggressively if required.
|
||||||
/// It is indirectly used to decide the automatic transform offset provided to <see cref="UpdateInitialTransforms"/>.
|
/// It is indirectly used to decide the automatic transform offset provided to <see cref="UpdateInitialTransforms"/>.
|
||||||
/// A more accurate <see cref="LifetimeStart"/> should be set inside <see cref="UpdateState"/> for an <see cref="ArmedState.Idle"/> state.
|
/// A more accurate <see cref="LifetimeStart"/> should be set for further optimisation (in <see cref="LoadComplete"/>, for example).
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected virtual double InitialLifetimeOffset => 10000;
|
protected virtual double InitialLifetimeOffset => 10000;
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current health.
|
/// The current health.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly BindableDouble Health = new BindableDouble { MinValue = 0, MaxValue = 1 };
|
public readonly BindableDouble Health = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current combo.
|
/// The current combo.
|
||||||
@ -233,6 +233,8 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
drawableRuleset.OnRevertResult += revertResult;
|
drawableRuleset.OnRevertResult += revertResult;
|
||||||
|
|
||||||
ApplyBeatmap(drawableRuleset.Beatmap);
|
ApplyBeatmap(drawableRuleset.Beatmap);
|
||||||
|
|
||||||
|
Reset(false);
|
||||||
SimulateAutoplay(drawableRuleset.Beatmap);
|
SimulateAutoplay(drawableRuleset.Beatmap);
|
||||||
Reset(true);
|
Reset(true);
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Screens;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Screens.Backgrounds;
|
using osu.Game.Screens.Backgrounds;
|
||||||
using osu.Game.Screens.Charts;
|
using osu.Game.Screens.Charts;
|
||||||
@ -44,6 +45,12 @@ namespace osu.Game.Screens.Menu
|
|||||||
[Resolved(canBeNull: true)]
|
[Resolved(canBeNull: true)]
|
||||||
private MusicController music { get; set; }
|
private MusicController music { get; set; }
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private LoginOverlay login { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
private BackgroundScreenDefault background;
|
private BackgroundScreenDefault background;
|
||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => background;
|
protected override BackgroundScreen CreateBackground() => background;
|
||||||
@ -133,6 +140,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
Beatmap.ValueChanged += beatmap_ValueChanged;
|
Beatmap.ValueChanged += beatmap_ValueChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool loginDisplayed;
|
||||||
|
|
||||||
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||||
{
|
{
|
||||||
base.LogoArriving(logo, resuming);
|
base.LogoArriving(logo, resuming);
|
||||||
@ -151,6 +160,21 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
sideFlashes.Delay(FADE_IN_DURATION).FadeIn(64, Easing.InQuint);
|
sideFlashes.Delay(FADE_IN_DURATION).FadeIn(64, Easing.InQuint);
|
||||||
}
|
}
|
||||||
|
else if (!api.IsLoggedIn)
|
||||||
|
{
|
||||||
|
logo.Action += displayLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool displayLogin()
|
||||||
|
{
|
||||||
|
if (!loginDisplayed)
|
||||||
|
{
|
||||||
|
Scheduler.AddDelayed(() => login?.Show(), 500);
|
||||||
|
loginDisplayed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LogoSuspending(OsuLogo logo)
|
protected override void LogoSuspending(OsuLogo logo)
|
||||||
|
@ -27,8 +27,8 @@ namespace osu.Game.Screens.Select
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
beatmap = value;
|
beatmap = value;
|
||||||
Leaderboard.Beatmap = beatmap?.BeatmapInfo;
|
|
||||||
Details.Beatmap = beatmap?.BeatmapInfo;
|
Details.Beatmap = beatmap?.BeatmapInfo;
|
||||||
|
Leaderboard.Beatmap = beatmap is DummyWorkingBeatmap ? null : beatmap?.BeatmapInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +83,12 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
|
|
||||||
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback)
|
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback)
|
||||||
{
|
{
|
||||||
|
if (Beatmap == null)
|
||||||
|
{
|
||||||
|
PlaceholderState = PlaceholderState.NoneSelected;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (Scope == BeatmapLeaderboardScope.Local)
|
if (Scope == BeatmapLeaderboardScope.Local)
|
||||||
{
|
{
|
||||||
var scores = scoreManager
|
var scores = scoreManager
|
||||||
@ -113,7 +119,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Beatmap?.OnlineBeatmapID == null || Beatmap?.Status <= BeatmapSetOnlineStatus.Pending)
|
if (Beatmap.OnlineBeatmapID == null || Beatmap?.Status <= BeatmapSetOnlineStatus.Pending)
|
||||||
{
|
{
|
||||||
PlaceholderState = PlaceholderState.Unavailable;
|
PlaceholderState = PlaceholderState.Unavailable;
|
||||||
return null;
|
return null;
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Text;
|
using osu.Framework.Text;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
@ -18,7 +17,7 @@ namespace osu.Game.Skinning
|
|||||||
Shadow = false;
|
Shadow = false;
|
||||||
UseFullGlyphHeight = false;
|
UseFullGlyphHeight = false;
|
||||||
|
|
||||||
Font = new FontUsage(font, OsuFont.DEFAULT_FONT_SIZE);
|
Font = new FontUsage(font, 1);
|
||||||
glyphStore = new LegacyGlyphStore(skin);
|
glyphStore = new LegacyGlyphStore(skin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,10 +36,6 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
var texture = skin.GetTexture($"{fontName}-{character}");
|
var texture = skin.GetTexture($"{fontName}-{character}");
|
||||||
|
|
||||||
if (texture != null)
|
|
||||||
// Approximate value that brings character sizing roughly in-line with stable
|
|
||||||
texture.ScaleAdjust *= 18;
|
|
||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.904.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.904.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2019.905.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2019.911.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
@ -118,8 +118,8 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.904.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.904.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2019.905.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2019.911.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.905.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.911.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
<PackageReference Include="SharpCompress" Version="0.24.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" />
|
||||||
|