Merge remote-tracking branch 'origin/master' into editor-mask-placement

# Conflicts:
#	osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
#	osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
#	osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
#	osu.Game/Rulesets/UI/RulesetContainer.cs
This commit is contained in:
smoogipoo
2018-10-16 17:28:16 +09:00
80 changed files with 683 additions and 624 deletions

View File

@ -27,9 +27,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="4.5.0" /> <PackageReference Include="System.IO.Packaging" Version="4.5.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.6" /> <PackageReference Include="ppy.squirrel.windows" Version="1.8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" /> <EmbeddedResource Include="lazer.ico" />

View File

@ -2,9 +2,10 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.10.1" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -11,6 +11,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
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 OpenTK;
namespace osu.Game.Rulesets.Catch.UI namespace osu.Game.Rulesets.Catch.UI
{ {
@ -18,9 +19,6 @@ namespace osu.Game.Rulesets.Catch.UI
{ {
public const float BASE_WIDTH = 512; public const float BASE_WIDTH = 512;
protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content;
private readonly CatcherArea catcherArea; private readonly CatcherArea catcherArea;
protected override bool UserScrollSpeedAdjustment => false; protected override bool UserScrollSpeedAdjustment => false;
@ -28,7 +26,6 @@ namespace osu.Game.Rulesets.Catch.UI
protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Constant; protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Constant;
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation) public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
: base(BASE_WIDTH)
{ {
Direction.Value = ScrollingDirection.Down; Direction.Value = ScrollingDirection.Down;
@ -37,27 +34,27 @@ namespace osu.Game.Rulesets.Catch.UI
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
base.Content.Anchor = Anchor.BottomLeft; Size = new Vector2(0.86f); // matches stable's vertical offset for catcher plate
base.Content.Origin = Anchor.BottomLeft;
base.Content.AddRange(new Drawable[] InternalChild = new PlayfieldAdjustmentContainer
{ {
explodingFruitContainer = new Container RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, explodingFruitContainer = new Container
}, {
catcherArea = new CatcherArea(difficulty) RelativeSizeAxes = Axes.Both,
{ },
GetVisualRepresentation = getVisualRepresentation, catcherArea = new CatcherArea(difficulty)
ExplodingFruitTarget = explodingFruitContainer, {
Anchor = Anchor.BottomLeft, GetVisualRepresentation = getVisualRepresentation,
Origin = Anchor.TopLeft, ExplodingFruitTarget = explodingFruitContainer,
}, Anchor = Anchor.BottomLeft,
content = new Container<Drawable> Origin = Anchor.TopLeft,
{ },
RelativeSizeAxes = Axes.Both, HitObjectContainer
}, }
}); };
VisibleTimeRange.Value = BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); VisibleTimeRange.Value = BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
} }

View File

@ -13,7 +13,6 @@ using osu.Game.Rulesets.Replays;
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;
using OpenTK;
namespace osu.Game.Rulesets.Catch.UI namespace osu.Game.Rulesets.Catch.UI
{ {
@ -32,8 +31,6 @@ namespace osu.Game.Rulesets.Catch.UI
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
protected override Vector2 PlayfieldArea => new Vector2(0.86f); // matches stable's vertical offset for catcher plate
public override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h) public override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
{ {
switch (h) switch (h)

View File

@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.UI
{ {
public class CatcherArea : Container public class CatcherArea : Container
{ {
public const float CATCHER_SIZE = 84; public const float CATCHER_SIZE = 100;
protected readonly Catcher MovableCatcher; protected readonly Catcher MovableCatcher;

View File

@ -0,0 +1,42 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
namespace osu.Game.Rulesets.Catch.UI
{
public class PlayfieldAdjustmentContainer : Container
{
protected override Container<Drawable> Content => content;
private readonly Container content;
public PlayfieldAdjustmentContainer()
{
InternalChild = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
FillAspectRatio = 4f / 3,
Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both }
};
}
/// <summary>
/// A <see cref="Container"/> which scales its content relative to a target width.
/// </summary>
private class ScalingContainer : Container
{
protected override void Update()
{
base.Update();
Scale = new Vector2(Parent.ChildSize.X / CatchPlayfield.BASE_WIDTH);
Size = Vector2.Divide(Vector2.One, Scale);
}
}
}
}

View File

@ -2,9 +2,10 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.10.1" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
{ {
base.InitialiseDefaults(); base.InitialiseDefaults();
Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0); Set(ManiaSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down); Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down);
} }

View File

@ -32,9 +32,8 @@ namespace osu.Game.Rulesets.Mania.Edit
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = Vector2.One
}; };
protected override Vector2 PlayfieldArea => Vector2.One;
} }
} }
} }

View File

@ -30,8 +30,6 @@ namespace osu.Game.Rulesets.Mania.UI
internal readonly Container TopLevelContainer; internal readonly Container TopLevelContainer;
private readonly Container explosionContainer; private readonly Container explosionContainer;
protected override Container<Drawable> Content => hitObjectArea;
public Column() public Column()
{ {
RelativeSizeAxes = Axes.Y; RelativeSizeAxes = Axes.Y;
@ -54,7 +52,10 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
hitObjectArea = new ColumnHitObjectArea { RelativeSizeAxes = Axes.Both }, hitObjectArea = new ColumnHitObjectArea(HitObjectContainer)
{
RelativeSizeAxes = Axes.Both,
},
explosionContainer = new Container explosionContainer = new Container
{ {
Name = "Hit explosions", Name = "Hit explosions",

View File

@ -8,28 +8,24 @@ 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.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using OpenTK.Graphics; using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.UI.Components namespace osu.Game.Rulesets.Mania.UI.Components
{ {
public class ColumnHitObjectArea : Container, IHasAccentColour public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour
{ {
private const float hit_target_height = 10; private const float hit_target_height = 10;
private const float hit_target_bar_height = 2; private const float hit_target_bar_height = 2;
private Container<Drawable> content;
protected override Container<Drawable> Content => content;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>(); private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private Container hitTargetLine; private readonly Container hitTargetLine;
private readonly Drawable hitTargetBar;
[BackgroundDependencyLoader] public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
private void load(IScrollingInfo scrollingInfo)
{ {
Drawable hitTargetBar;
InternalChildren = new[] InternalChildren = new[]
{ {
hitTargetBar = new Box hitTargetBar = new Box
@ -45,13 +41,13 @@ namespace osu.Game.Rulesets.Mania.UI.Components
Masking = true, Masking = true,
Child = new Box { RelativeSizeAxes = Axes.Both } Child = new Box { RelativeSizeAxes = Axes.Both }
}, },
content = new Container hitObjectContainer
{
Name = "Hit objects",
RelativeSizeAxes = Axes.Both,
},
}; };
}
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
{
direction.BindTo(scrollingInfo.Direction); direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(direction => direction.BindValueChanged(direction =>
{ {

View File

@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
@ -25,6 +26,8 @@ namespace osu.Game.Rulesets.Mania.UI
if (stageDefinitions.Count <= 0) if (stageDefinitions.Count <= 0)
throw new ArgumentException("Can't have zero or fewer stages."); throw new ArgumentException("Can't have zero or fewer stages.");
Size = new Vector2(1, 0.8f);
GridContainer playfieldGrid; GridContainer playfieldGrid;
AddInternal(playfieldGrid = new GridContainer AddInternal(playfieldGrid = new GridContainer
{ {

View File

@ -24,7 +24,6 @@ using osu.Game.Rulesets.Replays;
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;
using OpenTK;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
@ -110,8 +109,6 @@ namespace osu.Game.Rulesets.Mania.UI
} }
} }
protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay); protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
} }
} }

View File

@ -7,7 +7,7 @@ using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
public class ManiaScrollingPlayfield : ScrollingPlayfield public abstract class ManiaScrollingPlayfield : ScrollingPlayfield
{ {
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>(); private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();

View File

@ -30,8 +30,7 @@ namespace osu.Game.Rulesets.Mania.UI
public IReadOnlyList<Column> Columns => columnFlow.Children; public IReadOnlyList<Column> Columns => columnFlow.Children;
private readonly FillFlowContainer<Column> columnFlow; private readonly FillFlowContainer<Column> columnFlow;
protected override Container<Drawable> Content => barLineContainer; private readonly Container barLineContainer;
private readonly Container<Drawable> barLineContainer;
public Container<DrawableManiaJudgement> Judgements => judgements; public Container<DrawableManiaJudgement> Judgements => judgements;
private readonly JudgementContainer<DrawableManiaJudgement> judgements; private readonly JudgementContainer<DrawableManiaJudgement> judgements;
@ -105,6 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Child = HitObjectContainer
} }
}, },
judgements = new JudgementContainer<DrawableManiaJudgement> judgements = new JudgementContainer<DrawableManiaJudgement>

View File

@ -2,9 +2,10 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.10.1" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -10,6 +10,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
{ {
public class OsuBeatmapProcessor : BeatmapProcessor public class OsuBeatmapProcessor : BeatmapProcessor
{ {
private const int stack_distance = 3;
public OsuBeatmapProcessor(IBeatmap beatmap) public OsuBeatmapProcessor(IBeatmap beatmap)
: base(beatmap) : base(beatmap)
{ {
@ -18,17 +20,21 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
public override void PostProcess() public override void PostProcess()
{ {
base.PostProcess(); base.PostProcess();
applyStacking((Beatmap<OsuHitObject>)Beatmap);
var osuBeatmap = (Beatmap<OsuHitObject>)Beatmap;
// Reset stacking
foreach (var h in osuBeatmap.HitObjects)
h.StackHeight = 0;
if (Beatmap.BeatmapInfo.BeatmapVersion >= 6)
applyStacking(osuBeatmap);
else
applyStackingOld(osuBeatmap);
} }
private void applyStacking(Beatmap<OsuHitObject> beatmap) private void applyStacking(Beatmap<OsuHitObject> beatmap)
{ {
const int stack_distance = 3;
// Reset stacking
for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++)
beatmap.HitObjects[i].StackHeight = 0;
// Extend the end index to include objects they are stacked on // Extend the end index to include objects they are stacked on
int extendedEndIndex = beatmap.HitObjects.Count - 1; int extendedEndIndex = beatmap.HitObjects.Count - 1;
for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--) for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--)
@ -167,5 +173,40 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
} }
} }
} }
private void applyStackingOld(Beatmap<OsuHitObject> beatmap)
{
for (int i = 0; i < beatmap.HitObjects.Count; i++)
{
OsuHitObject currHitObject = beatmap.HitObjects[i];
if (currHitObject.StackHeight != 0 && !(currHitObject is Slider))
continue;
double startTime = (currHitObject as IHasEndTime)?.EndTime ?? currHitObject.StartTime;
int sliderStack = 0;
for (int j = i + 1; j < beatmap.HitObjects.Count; j++)
{
double stackThreshold = beatmap.HitObjects[i].TimePreempt * beatmap.BeatmapInfo.StackLeniency;
if (beatmap.HitObjects[j].StartTime - stackThreshold > startTime)
break;
if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.Position) < stack_distance)
{
currHitObject.StackHeight++;
startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime;
}
else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.EndPosition) < stack_distance)
{
//Case for sliders - bump notes down and right, rather than up and left.
sliderStack++;
beatmap.HitObjects[j].StackHeight -= sliderStack;
startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime;
}
}
}
}
} }
} }

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double sectionLength = section_length * timeRate; double sectionLength = section_length * timeRate;
// The first object doesn't generate a strain, so we begin with an incremented section end // The first object doesn't generate a strain, so we begin with an incremented section end
double currentSectionEnd = 2 * sectionLength; double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength;
foreach (OsuDifficultyHitObject h in difficultyBeatmap) foreach (OsuDifficultyHitObject h in difficultyBeatmap)
{ {

View File

@ -3,6 +3,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
@ -23,8 +24,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
{ {
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases. // Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
// This should probably happen before the objects reach the difficulty calculator. // This should probably happen before the objects reach the difficulty calculator.
objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime)); difficultyObjects = createDifficultyObjectEnumerator(objects.OrderBy(h => h.StartTime).ToList(), timeRate);
difficultyObjects = createDifficultyObjectEnumerator(objects, timeRate);
} }
/// <summary> /// <summary>

View File

@ -21,15 +21,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
public OsuHitObject BaseObject { get; } public OsuHitObject BaseObject { get; }
/// <summary> /// <summary>
/// Normalized distance from the <see cref="OsuHitObject.StackedPosition"/> of the previous <see cref="OsuDifficultyHitObject"/>. /// Normalized distance from the end position of the previous <see cref="OsuDifficultyHitObject"/> to the start position of this <see cref="OsuDifficultyHitObject"/>.
/// </summary> /// </summary>
public double Distance { get; private set; } public double JumpDistance { get; private set; }
/// <summary>
/// Normalized distance between the start and end position of the previous <see cref="OsuDifficultyHitObject"/>.
/// </summary>
public double TravelDistance { get; private set; }
/// <summary> /// <summary>
/// Milliseconds elapsed since the StartTime of the previous <see cref="OsuDifficultyHitObject"/>. /// Milliseconds elapsed since the StartTime of the previous <see cref="OsuDifficultyHitObject"/>.
/// </summary> /// </summary>
public double DeltaTime { get; private set; } public double DeltaTime { get; private set; }
/// <summary>
/// Milliseconds elapsed since the start time of the previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 50ms.
/// </summary>
public double StrainTime { get; private set; }
private readonly OsuHitObject lastObject; private readonly OsuHitObject lastObject;
private readonly double timeRate; private readonly double timeRate;
@ -51,31 +61,37 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
private void setDistances() private void setDistances()
{ {
// We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps. // We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps.
double scalingFactor = normalized_radius / BaseObject.Radius; float scalingFactor = normalized_radius / (float)BaseObject.Radius;
if (BaseObject.Radius < 30) if (BaseObject.Radius < 30)
{ {
double smallCircleBonus = Math.Min(30 - BaseObject.Radius, 5) / 50; float smallCircleBonus = Math.Min(30 - (float)BaseObject.Radius, 5) / 50;
scalingFactor *= 1 + smallCircleBonus; scalingFactor *= 1 + smallCircleBonus;
} }
Vector2 lastCursorPosition = lastObject.StackedPosition; Vector2 lastCursorPosition = lastObject.StackedPosition;
float lastTravelDistance = 0;
var lastSlider = lastObject as Slider; var lastSlider = lastObject as Slider;
if (lastSlider != null) if (lastSlider != null)
{ {
computeSliderCursorPosition(lastSlider); computeSliderCursorPosition(lastSlider);
lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition; lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition;
lastTravelDistance = lastSlider.LazyTravelDistance;
} }
Distance = (lastTravelDistance + (BaseObject.StackedPosition - lastCursorPosition).Length) * scalingFactor; // Don't need to jump to reach spinners
if (!(BaseObject is Spinner))
JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
// Todo: BUG!!! Last slider's travel distance is considered ONLY IF we ourselves are also a slider!
if (BaseObject is Slider)
TravelDistance = (lastSlider?.LazyTravelDistance ?? 0) * scalingFactor;
} }
private void setTimingValues() private void setTimingValues()
{ {
// Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure. DeltaTime = (BaseObject.StartTime - lastObject.StartTime) / timeRate;
DeltaTime = Math.Max(50, (BaseObject.StartTime - lastObject.StartTime) / timeRate);
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
StrainTime = Math.Max(50, DeltaTime);
} }
private void computeSliderCursorPosition(Slider slider) private void computeSliderCursorPosition(Slider slider)
@ -87,8 +103,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
float approxFollowCircleRadius = (float)(slider.Radius * 3); float approxFollowCircleRadius = (float)(slider.Radius * 3);
var computeVertex = new Action<double>(t => var computeVertex = new Action<double>(t =>
{ {
double progress = ((int)t - (int)slider.StartTime) / (float)(int)slider.SpanDuration;
if (progress % 2 > 1)
progress = 1 - progress % 1;
else
progress = progress % 1;
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version) // ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
var diff = slider.StackedPositionAt(t) - slider.LazyEndPosition.Value; var diff = slider.StackedPosition + slider.Curve.PositionAt(progress) - slider.LazyEndPosition.Value;
float dist = diff.Length; float dist = diff.Length;
if (dist > approxFollowCircleRadius) if (dist > approxFollowCircleRadius)

View File

@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
protected override double SkillMultiplier => 26.25; protected override double SkillMultiplier => 26.25;
protected override double StrainDecayBase => 0.15; protected override double StrainDecayBase => 0.15;
protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime; protected override double StrainValueOf(OsuDifficultyHitObject current)
=> (Math.Pow(current.TravelDistance, 0.99) + Math.Pow(current.JumpDistance, 0.99)) / current.StrainTime;
} }
} }

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
protected override double StrainValueOf(OsuDifficultyHitObject current) protected override double StrainValueOf(OsuDifficultyHitObject current)
{ {
double distance = current.Distance; double distance = current.TravelDistance + current.JumpDistance;
double speedValue; double speedValue;
if (distance > single_spacing_threshold) if (distance > single_spacing_threshold)
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
else else
speedValue = 0.95; speedValue = 0.95;
return speedValue / current.DeltaTime; return speedValue / current.StrainTime;
} }
} }
} }

View File

@ -28,9 +28,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{ {
} }
protected override Vector2 PlayfieldArea => Vector2.One;
protected override CursorContainer CreateCursor() => null; protected override CursorContainer CreateCursor() => null;
} }
protected override Playfield CreatePlayfield() => new OsuPlayfield { Size = Vector2.One };
} }
} }

View File

@ -3,6 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Edit.Tools;
@ -28,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Edit
new HitCircleCompositionTool(), new HitCircleCompositionTool(),
}; };
protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both }; protected override Container CreateLayerContainer() => new PlayfieldAdjustmentContainer { RelativeSizeAxes = Axes.Both };
public override SelectionMask CreateMaskFor(DrawableHitObject hitObject) public override SelectionMask CreateMaskFor(DrawableHitObject hitObject)
{ {

View File

@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Osu.Mods
var osuObject = (OsuHitObject)drawable.HitObject; var osuObject = (OsuHitObject)drawable.HitObject;
Vector2 origin = drawable.Position; Vector2 origin = drawable.Position;
// Wiggle the repeat points with the slider instead of independently.
// Also fixes an issue with repeat points being positioned incorrectly.
if (osuObject is RepeatPoint)
return;
Random objRand = new Random((int)osuObject.StartTime); Random objRand = new Random((int)osuObject.StartTime);
// Wiggle all objects during TimePreempt // Wiggle all objects during TimePreempt

View File

@ -8,26 +8,23 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Textures;
using OpenTK.Graphics.ES30; using OpenTK.Graphics.ES30;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using OpenTK; using OpenTK;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public class SliderBody : Container, ISliderProgress public class SliderBody : Container, ISliderProgress
{ {
private readonly Path path; private readonly SliderPath path;
private readonly BufferedContainer container; private readonly BufferedContainer container;
public float PathWidth public float PathWidth
{ {
get { return path.PathWidth; } get => path.PathWidth;
set { path.PathWidth = value; } set => path.PathWidth = value;
} }
/// <summary> /// <summary>
@ -43,48 +40,40 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public double? SnakedStart { get; private set; } public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; } public double? SnakedEnd { get; private set; }
private Color4 accentColour = Color4.White;
/// <summary> /// <summary>
/// Used to colour the path. /// Used to colour the path.
/// </summary> /// </summary>
public Color4 AccentColour public Color4 AccentColour
{ {
get { return accentColour; } get => path.AccentColour;
set set
{ {
if (accentColour == value) if (path.AccentColour == value)
return; return;
accentColour = value; path.AccentColour = value;
if (LoadState >= LoadState.Ready) container.ForceRedraw();
reloadTexture();
} }
} }
private Color4 borderColour = Color4.White;
/// <summary> /// <summary>
/// Used to colour the path border. /// Used to colour the path border.
/// </summary> /// </summary>
public new Color4 BorderColour public new Color4 BorderColour
{ {
get { return borderColour; } get => path.BorderColour;
set set
{ {
if (borderColour == value) if (path.BorderColour == value)
return; return;
borderColour = value; path.BorderColour = value;
if (LoadState >= LoadState.Ready) container.ForceRedraw();
reloadTexture();
} }
} }
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad; public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
private int textureWidth => (int)PathWidth * 2;
private Vector2 topLeftOffset; private Vector2 topLeftOffset;
private readonly Slider slider; private readonly Slider slider;
@ -101,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
CacheDrawnFrameBuffer = true, CacheDrawnFrameBuffer = true,
Children = new Drawable[] Children = new Drawable[]
{ {
path = new Path path = new SliderPath
{ {
Blending = BlendingMode.None, Blending = BlendingMode.None,
}, },
@ -134,46 +123,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
reloadTexture();
computeSize(); computeSize();
} }
private void reloadTexture()
{
var texture = new Texture(textureWidth, 1);
//initialise background
var raw = new Image<Rgba32>(textureWidth, 1);
const float aa_portion = 0.02f;
const float border_portion = 0.128f;
const float gradient_portion = 1 - border_portion;
const float opacity_at_centre = 0.3f;
const float opacity_at_edge = 0.8f;
for (int i = 0; i < textureWidth; i++)
{
float progress = (float)i / (textureWidth - 1);
if (progress <= border_portion)
{
raw[i, 0] = new Rgba32(BorderColour.R, BorderColour.G, BorderColour.B, Math.Min(progress / aa_portion, 1) * BorderColour.A);
}
else
{
progress -= border_portion;
raw[i, 0] = new Rgba32(AccentColour.R, AccentColour.G, AccentColour.B,
(opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * AccentColour.A);
}
}
texture.SetData(new TextureUpload(raw));
path.Texture = texture;
container.ForceRedraw();
}
private void computeSize() private void computeSize()
{ {
// Generate the entire curve // Generate the entire curve
@ -226,5 +178,53 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
SetRange(start, end); SetRange(start, end);
} }
private class SliderPath : SmoothPath
{
private const float border_portion = 0.128f;
private const float gradient_portion = 1 - border_portion;
private const float opacity_at_centre = 0.3f;
private const float opacity_at_edge = 0.8f;
private Color4 borderColour = Color4.White;
public Color4 BorderColour
{
get => borderColour;
set
{
if (borderColour == value)
return;
borderColour = value;
InvalidateTexture();
}
}
private Color4 accentColour = Color4.White;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
InvalidateTexture();
}
}
protected override Color4 ColourAt(float position)
{
if (position <= border_portion)
return BorderColour;
position -= border_portion;
return new Color4(AccentColour.R, AccentColour.G, AccentColour.B, (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * position / gradient_portion) * AccentColour.A);
}
}
} }
} }

View File

@ -45,10 +45,10 @@ namespace osu.Game.Rulesets.Osu
public enum OsuAction public enum OsuAction
{ {
[Description("Left Button")] [Description("Left button")]
LeftButton, LeftButton,
[Description("Right Button")] [Description("Right button")]
RightButton RightButton
} }
} }

View File

@ -23,29 +23,35 @@ namespace osu.Game.Rulesets.Osu.UI
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
public OsuPlayfield() public OsuPlayfield()
: base(BASE_SIZE.X)
{ {
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
AddRange(new Drawable[] Size = new Vector2(0.75f);
InternalChild = new PlayfieldAdjustmentContainer
{ {
connectionLayer = new FollowPointRenderer RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, connectionLayer = new FollowPointRenderer
Depth = 2, {
}, RelativeSizeAxes = Axes.Both,
judgementLayer = new JudgementContainer<DrawableOsuJudgement> Depth = 2,
{ },
RelativeSizeAxes = Axes.Both, judgementLayer = new JudgementContainer<DrawableOsuJudgement>
Depth = 1, {
}, RelativeSizeAxes = Axes.Both,
approachCircles = new Container Depth = 1,
{ },
RelativeSizeAxes = Axes.Both, HitObjectContainer,
Depth = -1, approachCircles = new Container
}, {
}); RelativeSizeAxes = Axes.Both,
Depth = -1,
},
}
};
} }
public override void Add(DrawableHitObject h) public override void Add(DrawableHitObject h)

View File

@ -4,7 +4,6 @@
using System.Linq; using System.Linq;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Input; using osu.Framework.Input;
using OpenTK;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Input.Handlers; using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
@ -58,12 +57,6 @@ namespace osu.Game.Rulesets.Osu.UI
} }
} }
protected override Vector2 GetAspectAdjustedSize()
{
var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y);
return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y);
}
protected override CursorContainer CreateCursor() => new GameplayCursor(); protected override CursorContainer CreateCursor() => new GameplayCursor();
} }
} }

View File

@ -0,0 +1,42 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
namespace osu.Game.Rulesets.Osu.UI
{
public class PlayfieldAdjustmentContainer : Container
{
protected override Container<Drawable> Content => content;
private readonly Container content;
public PlayfieldAdjustmentContainer()
{
InternalChild = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
FillAspectRatio = 4f / 3,
Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both }
};
}
/// <summary>
/// A <see cref="Container"/> which scales its content relative to a target width.
/// </summary>
private class ScalingContainer : Container
{
protected override void Update()
{
base.Update();
Scale = new Vector2(Parent.ChildSize.X / OsuPlayfield.BASE_SIZE.X);
Size = Vector2.Divide(Vector2.One, Scale);
}
}
}
}

View File

@ -2,9 +2,10 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.10.1" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -17,13 +17,13 @@ namespace osu.Game.Rulesets.Taiko
public enum TaikoAction public enum TaikoAction
{ {
[Description("Left (Rim)")] [Description("Left (rim)")]
LeftRim, LeftRim,
[Description("Left (Centre)")] [Description("Left (centre)")]
LeftCentre, LeftCentre,
[Description("Right (Centre)")] [Description("Right (centre)")]
RightCentre, RightCentre,
[Description("Right (Rim)")] [Description("Right (rim)")]
RightRim RightRim
} }
} }

View File

@ -0,0 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using OpenTK;
namespace osu.Game.Rulesets.Taiko.UI
{
public class PlayfieldAdjustmentContainer : Container
{
private const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768;
private const float default_aspect = 16f / 9f;
protected override void Update()
{
base.Update();
float aspectAdjust = MathHelper.Clamp(Parent.ChildSize.X / Parent.ChildSize.Y, 0.4f, 4) / default_aspect;
Size = new Vector2(1, default_relative_height * aspectAdjust);
}
}
}

View File

@ -47,9 +47,6 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer; private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer; private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer;
protected override Container<Drawable> Content => content;
private readonly Container content;
private readonly Container topLevelHitContainer; private readonly Container topLevelHitContainer;
private readonly Container barlineContainer; private readonly Container barlineContainer;
@ -64,140 +61,147 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
Direction.Value = ScrollingDirection.Left; Direction.Value = ScrollingDirection.Left;
AddRangeInternal(new Drawable[] InternalChild = new PlayfieldAdjustmentContainer
{ {
backgroundContainer = new Container Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{ {
Name = "Transparent playfield background", backgroundContainer = new Container
RelativeSizeAxes = Axes.Both,
Masking = true,
EdgeEffect = new EdgeEffectParameters
{ {
Type = EdgeEffectType.Shadow, Name = "Transparent playfield background",
Colour = Color4.Black.Opacity(0.2f), RelativeSizeAxes = Axes.Both,
Radius = 5, Masking = true,
}, EdgeEffect = new EdgeEffectParameters
Children = new Drawable[]
{
background = new Box
{ {
RelativeSizeAxes = Axes.Both, Type = EdgeEffectType.Shadow,
Alpha = 0.6f Colour = Color4.Black.Opacity(0.2f),
Radius = 5,
}, },
} Children = new Drawable[]
},
new Container
{
Name = "Right area",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = left_area_size },
Children = new Drawable[]
{
new Container
{ {
Name = "Masked elements before hit objects", background = new Box
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
Masking = true,
Children = new Drawable[]
{ {
hitExplosionContainer = new Container<HitExplosion> RelativeSizeAxes = Axes.Both,
{ Alpha = 0.6f
RelativeSizeAxes = Axes.Both, },
FillMode = FillMode.Fit,
Blending = BlendingMode.Additive,
},
new HitTarget
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit
}
}
},
barlineContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }
},
content = new Container
{
Name = "Hit objects",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
Masking = true
},
kiaiExplosionContainer = new Container<KiaiHitExplosion>
{
Name = "Kiai hit explosions",
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
Blending = BlendingMode.Additive
},
judgementContainer = new JudgementContainer<DrawableTaikoJudgement>
{
Name = "Judgements",
RelativeSizeAxes = Axes.Y,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
Blending = BlendingMode.Additive
},
}
},
overlayBackgroundContainer = new Container
{
Name = "Left overlay",
RelativeSizeAxes = Axes.Y,
Size = new Vector2(left_area_size, 1),
Children = new Drawable[]
{
overlayBackground = new Box
{
RelativeSizeAxes = Axes.Both,
},
new InputDrum(controlPoints)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Scale = new Vector2(0.9f),
Margin = new MarginPadding { Right = 20 }
},
new Box
{
Anchor = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = 10,
Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
},
}
},
new Container
{
Name = "Border",
RelativeSizeAxes = Axes.Both,
Masking = true,
MaskingSmoothness = 0,
BorderThickness = 2,
AlwaysPresent = true,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
} }
},
new Container
{
Name = "Right area",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = left_area_size },
Children = new Drawable[]
{
new Container
{
Name = "Masked elements before hit objects",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
Masking = true,
Children = new Drawable[]
{
hitExplosionContainer = new Container<HitExplosion>
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Blending = BlendingMode.Additive,
},
new HitTarget
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit
}
}
},
barlineContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }
},
new Container
{
Name = "Hit objects",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
Masking = true,
Child = HitObjectContainer
},
kiaiExplosionContainer = new Container<KiaiHitExplosion>
{
Name = "Kiai hit explosions",
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
Blending = BlendingMode.Additive
},
judgementContainer = new JudgementContainer<DrawableTaikoJudgement>
{
Name = "Judgements",
RelativeSizeAxes = Axes.Y,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
Blending = BlendingMode.Additive
},
}
},
overlayBackgroundContainer = new Container
{
Name = "Left overlay",
RelativeSizeAxes = Axes.Y,
Size = new Vector2(left_area_size, 1),
Children = new Drawable[]
{
overlayBackground = new Box
{
RelativeSizeAxes = Axes.Both,
},
new InputDrum(controlPoints)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Scale = new Vector2(0.9f),
Margin = new MarginPadding { Right = 20 }
},
new Box
{
Anchor = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = 10,
Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
},
}
},
new Container
{
Name = "Border",
RelativeSizeAxes = Axes.Both,
Masking = true,
MaskingSmoothness = 0,
BorderThickness = 2,
AlwaysPresent = true,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
topLevelHitContainer = new Container
{
Name = "Top level hit objects",
RelativeSizeAxes = Axes.Both,
} }
},
topLevelHitContainer = new Container
{
Name = "Top level hit objects",
RelativeSizeAxes = Axes.Both,
} }
}); };
VisibleTimeRange.Value = 6000; VisibleTimeRange.Value = 7000;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics;
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.Objects.Types;
@ -13,7 +12,6 @@ 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 OpenTK;
using System.Linq; using System.Linq;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Input.Handlers; using osu.Game.Input.Handlers;
@ -74,27 +72,11 @@ namespace osu.Game.Rulesets.Taiko.UI
} }
} }
protected override Vector2 GetAspectAdjustedSize()
{
const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768;
const float default_aspect = 16f / 9f;
float aspectAdjust = MathHelper.Clamp(DrawWidth / DrawHeight, 0.4f, 4) / default_aspect;
return new Vector2(1, default_relative_height * aspectAdjust);
}
protected override Vector2 PlayfieldArea => Vector2.One;
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo) protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo);
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
};
public override DrawableHitObject<TaikoHitObject> GetVisualRepresentation(TaikoHitObject h) public override DrawableHitObject<TaikoHitObject> GetVisualRepresentation(TaikoHitObject h)
{ {

View File

@ -165,7 +165,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
} }
[Test] [Test]
public void TestDecodeBeatmapColors() public void TestDecodeBeatmapColours()
{ {
var decoder = new LegacySkinDecoder(); var decoder = new LegacySkinDecoder();
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
@ -181,6 +181,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
new Color4(128, 255, 128, 255), new Color4(128, 255, 128, 255),
new Color4(255, 187, 255, 255), new Color4(255, 187, 255, 255),
new Color4(255, 177, 140, 255), new Color4(255, 177, 140, 255),
new Color4(100, 100, 100, 100),
}; };
Assert.AreEqual(expectedColors.Length, comboColors.Count); Assert.AreEqual(expectedColors.Length, comboColors.Count);
for (int i = 0; i < expectedColors.Length; i++) for (int i = 0; i < expectedColors.Length; i++)

View File

@ -101,6 +101,7 @@ Combo3 : 128,255,255
Combo4 : 128,255,128 Combo4 : 128,255,128
Combo5 : 255,187,255 Combo5 : 255,187,255
Combo6 : 255,177,140 Combo6 : 255,177,140
Combo7 : 100,100,100,100
[HitObjects] [HitObjects]
192,168,956,6,0,P|184:128|200:80,1,90,4|0,1:2|0:0,0:0:0:0: 192,168,956,6,0,P|184:128|200:80,1,90,4|0,1:2|0:0,0:0:0:0:

View File

@ -121,14 +121,21 @@ namespace osu.Game.Tests.Visual
Direction = direction; Direction = direction;
Padding = new MarginPadding(2); Padding = new MarginPadding(2);
Content.Masking = true;
AddInternal(new Box InternalChildren = new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, new Box
Alpha = 0.5f, {
Depth = float.MaxValue RelativeSizeAxes = Axes.Both,
}); Alpha = 0.5f,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Child = HitObjectContainer
}
};
} }
} }

View File

@ -2,9 +2,11 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> <PackageReference Include="DeepEqual" Version="1.6.0" />
<PackageReference Include="NUnit" Version="3.10.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -16,7 +16,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <summary> /// <summary>
/// The beat length at this control point. /// The beat length at this control point.
/// </summary> /// </summary>
public double BeatLength public virtual double BeatLength
{ {
get => beatLength; get => beatLength;
set => beatLength = MathHelper.Clamp(value, 6, 60000); set => beatLength = MathHelper.Clamp(value, 6, 60000);

View File

@ -318,12 +318,12 @@ namespace osu.Game.Beatmaps.Formats
if (timingChange) if (timingChange)
{ {
handleTimingControlPoint(new TimingControlPoint var controlPoint = CreateTimingControlPoint();
{ controlPoint.Time = time;
Time = time, controlPoint.BeatLength = beatLength;
BeatLength = beatLength, controlPoint.TimeSignature = timeSignature;
TimeSignature = timeSignature
}); handleTimingControlPoint(controlPoint);
} }
handleDifficultyControlPoint(new DifficultyControlPoint handleDifficultyControlPoint(new DifficultyControlPoint
@ -418,6 +418,8 @@ namespace osu.Game.Beatmaps.Formats
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0); private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
protected virtual TimingControlPoint CreateTimingControlPoint() => new TimingControlPoint();
[Flags] [Flags]
internal enum EffectFlags internal enum EffectFlags
{ {

View File

@ -85,13 +85,19 @@ namespace osu.Game.Beatmaps.Formats
string[] split = pair.Value.Split(','); string[] split = pair.Value.Split(',');
if (split.Length != 3) if (split.Length != 3 && split.Length != 4)
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}"); throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B or R,G,B,A): {pair.Value}");
if (!byte.TryParse(split[0], out var r) || !byte.TryParse(split[1], out var g) || !byte.TryParse(split[2], out var b)) Color4 colour;
try
{
colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), split.Length == 4 ? byte.Parse(split[3]) : (byte)255);
}
catch (Exception e)
{
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
}
Color4 colour = new Color4(r, g, b, 255);
if (isCombo) if (isCombo)
{ {

View File

@ -0,0 +1,37 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Beatmaps.Formats
{
/// <summary>
/// A <see cref="LegacyBeatmapDecoder"/> built for difficulty calculation of legacy <see cref="Beatmap"/>s
/// <remarks>
/// To use this, the decoder must be registered by the application through <see cref="LegacyDifficultyCalculatorBeatmapDecoder.Register"/>.
/// Doing so will override any existing <see cref="Beatmap"/> decoders.
/// </remarks>
/// </summary>
public class LegacyDifficultyCalculatorBeatmapDecoder : LegacyBeatmapDecoder
{
public LegacyDifficultyCalculatorBeatmapDecoder(int version = LATEST_VERSION)
: base(version)
{
ApplyOffsets = false;
}
public new static void Register()
{
AddDecoder<Beatmap>(@"osu file format v", m => new LegacyDifficultyCalculatorBeatmapDecoder(int.Parse(m.Split('v').Last())));
}
protected override TimingControlPoint CreateTimingControlPoint()
=> new LegacyDifficultyCalculatorControlPoint();
private class LegacyDifficultyCalculatorControlPoint : TimingControlPoint
{
public override double BeatLength { get; set; } = 1000;
}
}
}

View File

@ -69,7 +69,7 @@ namespace osu.Game.Graphics.UserInterface
{ {
Masking = true, Masking = true,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = path = new Path { RelativeSizeAxes = Axes.Both, PathWidth = 1 } Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathWidth = 1 }
}); });
} }

View File

@ -51,6 +51,8 @@ namespace osu.Game.Graphics.UserInterface
#region OsuDropdownMenu #region OsuDropdownMenu
protected class OsuDropdownMenu : DropdownMenu, IHasAccentColour protected class OsuDropdownMenu : DropdownMenu, IHasAccentColour
{ {
public override bool HandleNonPositionalInput => State == MenuState.Open;
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
public OsuDropdownMenu() public OsuDropdownMenu()
{ {

View File

@ -71,17 +71,17 @@ namespace osu.Game.Input.Bindings
ToggleSettings, ToggleSettings,
[Description("Toggle osu!direct")] [Description("Toggle osu!direct")]
ToggleDirect, ToggleDirect,
[Description("Increase Volume")] [Description("Increase volume")]
IncreaseVolume, IncreaseVolume,
[Description("Decrease Volume")] [Description("Decrease volume")]
DecreaseVolume, DecreaseVolume,
[Description("Toggle mute")] [Description("Toggle mute")]
ToggleMute, ToggleMute,
// In-Game Keybindings // In-Game Keybindings
[Description("Skip Cutscene")] [Description("Skip cutscene")]
SkipCutscene, SkipCutscene,
[Description("Quick Retry (Hold)")] [Description("Quick retry (hold)")]
QuickRetry, QuickRetry,
[Description("Take screenshot")] [Description("Take screenshot")]

View File

@ -65,7 +65,8 @@ namespace osu.Game
private BeatmapSetOverlay beatmapSetOverlay; private BeatmapSetOverlay beatmapSetOverlay;
private ScreenshotManager screenshotManager; [Cached]
private readonly ScreenshotManager screenshotManager = new ScreenshotManager();
protected RavenLogger RavenLogger; protected RavenLogger RavenLogger;
@ -289,9 +290,6 @@ namespace osu.Game
protected override void LoadComplete() protected override void LoadComplete()
{ {
// this needs to be cached before base.LoadComplete as it is used by MenuCursorContainer.
dependencies.Cache(screenshotManager = new ScreenshotManager());
base.LoadComplete(); base.LoadComplete();
// The next time this is updated is in UpdateAfterChildren, which occurs too late and results // The next time this is updated is in UpdateAfterChildren, which occurs too late and results

View File

@ -291,7 +291,7 @@ namespace osu.Game.Overlays
messageRequest?.Cancel(); messageRequest?.Cancel();
ListChannelsRequest req = new ListChannelsRequest(); ListChannelsRequest req = new ListChannelsRequest();
req.Success += delegate (List<Channel> channels) req.Success += delegate(List<Channel> channels)
{ {
AvailableChannels = channels; AvailableChannels = channels;
@ -323,10 +323,7 @@ namespace osu.Game.Overlays
protected Channel CurrentChannel protected Channel CurrentChannel
{ {
get get { return currentChannel; }
{
return currentChannel;
}
set set
{ {
@ -445,13 +442,7 @@ namespace osu.Game.Overlays
if (updates?.Presence != null) if (updates?.Presence != null)
{ {
foreach (var channel in updates.Presence) foreach (var channel in updates.Presence)
{ addChannel(AvailableChannels.Find(c => c.Id == channel.Id));
if (careChannels.Find(c => c.Id == channel.Id) == null)
{
channel.Joined.Value = true;
addChannel(channel);
}
}
foreach (var group in updates.Messages.GroupBy(m => m.ChannelId)) foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
@ -462,10 +453,7 @@ namespace osu.Game.Overlays
fetchReq = null; fetchReq = null;
}; };
fetchReq.Failure += delegate fetchReq.Failure += delegate { fetchReq = null; };
{
fetchReq = null;
};
api.Queue(fetchReq); api.Queue(fetchReq);
} }

View File

@ -196,10 +196,16 @@ namespace osu.Game.Overlays
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint); playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
} }
private ScheduledDelegate seekDelegate;
private void attemptSeek(double progress) private void attemptSeek(double progress)
{ {
if (!beatmap.Disabled) seekDelegate?.Cancel();
current?.Track.Seek(progress); seekDelegate = Schedule(() =>
{
if (!beatmap.Disabled)
current?.Track.Seek(progress);
});
} }
private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index) private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index)

View File

@ -386,10 +386,13 @@ namespace osu.Game.Overlays.Profile
infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), boldItalic); infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), boldItalic);
} }
infoTextLeft.NewLine(); if (user.LastVisit.HasValue)
infoTextLeft.AddText("Last seen ", lightText); {
infoTextLeft.AddText(new DrawableDate(user.LastVisit), boldItalic); infoTextLeft.NewLine();
infoTextLeft.NewParagraph(); infoTextLeft.AddText("Last seen ", lightText);
infoTextLeft.AddText(new DrawableDate(user.LastVisit.Value), boldItalic);
infoTextLeft.NewParagraph();
}
if (user.PlayStyle?.Length > 0) if (user.PlayStyle?.Length > 0)
{ {

View File

@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
{ {
new SettingsSlider<double, OffsetSlider> new SettingsSlider<double, OffsetSlider>
{ {
LabelText = "Audio Offset", LabelText = "Audio offset",
Bindable = config.GetBindable<double>(OsuSetting.AudioOffset), Bindable = config.GetBindable<double>(OsuSetting.AudioOffset),
KeyboardStep = 1f KeyboardStep = 1f
}, },

View File

@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
Children = new Drawable[] Children = new Drawable[]
{ {
new SettingsSlider<double> { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f }, new SettingsSlider<double> { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f },
new SettingsSlider<double> { LabelText = "Master (Window Inactive)", Bindable = config.GetBindable<double>(OsuSetting.VolumeInactive), KeyboardStep = 0.01f }, new SettingsSlider<double> { LabelText = "Master (window inactive)", Bindable = config.GetBindable<double>(OsuSetting.VolumeInactive), KeyboardStep = 0.01f },
new SettingsSlider<double> { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.01f }, new SettingsSlider<double> { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.01f },
new SettingsSlider<double> { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f }, new SettingsSlider<double> { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f },
}; };

View File

@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{ {
new SettingsButton new SettingsButton
{ {
Text = "Key Configuration", Text = "Key configuration",
Action = keyConfig.ToggleVisibility Action = keyConfig.ToggleVisibility
}, },
}; };

View File

@ -26,12 +26,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{ {
new SettingsCheckbox new SettingsCheckbox
{ {
LabelText = "Raw Input", LabelText = "Raw input",
Bindable = rawInputToggle Bindable = rawInputToggle
}, },
sensitivity = new SensitivitySetting sensitivity = new SensitivitySetting
{ {
LabelText = "Cursor Sensitivity", LabelText = "Cursor sensitivity",
Bindable = config.GetBindable<double>(FrameworkSetting.CursorSensitivity) Bindable = config.GetBindable<double>(FrameworkSetting.CursorSensitivity)
}, },
new SettingsCheckbox new SettingsCheckbox

View File

@ -105,6 +105,8 @@ namespace osu.Game.Overlays.Toolbar
public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput; public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput;
public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput; public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput;
public override bool PropagatePositionalInputSubTree => !ruleset.Disabled && base.PropagatePositionalInputSubTree;
private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300);
protected override void Update() protected override void Update()

View File

@ -189,6 +189,6 @@ namespace osu.Game.Rulesets.Edit
/// <summary> /// <summary>
/// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>. /// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.
/// </summary> /// </summary>
protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both }; protected virtual Container CreateLayerContainer() => new Container { RelativeSizeAxes = Axes.Both };
} }
} }

View File

@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Mods
{ {
/// <summary> /// <summary>
/// Applies this <see cref="IApplicableToDrawableHitObjects"/> to a list of <see cref="DrawableHitObject"/>s. /// Applies this <see cref="IApplicableToDrawableHitObjects"/> to a list of <see cref="DrawableHitObject"/>s.
/// This will only be invoked with top-level <see cref="DrawableHitObject"/>s. Access <see cref="DrawableHitObject.NestedHitObjects"/> if adjusting nested objects is necessary.
/// </summary> /// </summary>
/// <param name="drawables">The list of <see cref="DrawableHitObject"/>s to apply to.</param> /// <param name="drawables">The list of <see cref="DrawableHitObject"/>s to apply to.</param>
void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables); void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables);

View File

@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState; public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState;
/// <summary> /// <summary>
/// Plays all the hitsounds for this <see cref="DrawableHitObject"/>. /// Plays all the hit sounds for this <see cref="DrawableHitObject"/>.
/// </summary> /// </summary>
public void PlaySamples() => Samples?.Play(); public void PlaySamples() => Samples?.Play();

View File

@ -1,10 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Lists; using osu.Framework.Lists;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -58,10 +56,10 @@ namespace osu.Game.Rulesets.Objects
/// </summary> /// </summary>
public HitWindows HitWindows { get; set; } public HitWindows HitWindows { get; set; }
private readonly Lazy<SortedList<HitObject>> nestedHitObjects = new Lazy<SortedList<HitObject>>(() => new SortedList<HitObject>((h1, h2) => h1.StartTime.CompareTo(h2.StartTime))); private readonly SortedList<HitObject> nestedHitObjects = new SortedList<HitObject>(compareObjects);
[JsonIgnore] [JsonIgnore]
public IReadOnlyList<HitObject> NestedHitObjects => nestedHitObjects.Value; public IReadOnlyList<HitObject> NestedHitObjects => nestedHitObjects;
/// <summary> /// <summary>
/// Applies default values to this HitObject. /// Applies default values to this HitObject.
@ -72,18 +70,14 @@ namespace osu.Game.Rulesets.Objects
{ {
ApplyDefaultsToSelf(controlPointInfo, difficulty); ApplyDefaultsToSelf(controlPointInfo, difficulty);
if (nestedHitObjects.IsValueCreated) nestedHitObjects.Clear();
nestedHitObjects.Value.Clear();
CreateNestedHitObjects(); CreateNestedHitObjects();
if (nestedHitObjects.IsValueCreated) foreach (var h in nestedHitObjects)
{ {
nestedHitObjects.Value.ForEach(h => h.HitWindows = HitWindows;
{ h.ApplyDefaults(controlPointInfo, difficulty);
h.HitWindows = HitWindows;
h.ApplyDefaults(controlPointInfo, difficulty);
});
} }
} }
@ -104,7 +98,7 @@ namespace osu.Game.Rulesets.Objects
{ {
} }
protected void AddNested(HitObject hitObject) => nestedHitObjects.Value.Add(hitObject); protected void AddNested(HitObject hitObject) => nestedHitObjects.Add(hitObject);
/// <summary> /// <summary>
/// Creates the <see cref="Judgement"/> that represents the scoring information for this <see cref="HitObject"/>. /// Creates the <see cref="Judgement"/> that represents the scoring information for this <see cref="HitObject"/>.
@ -120,5 +114,7 @@ namespace osu.Game.Rulesets.Objects
/// </para> /// </para>
/// </summary> /// </summary>
protected virtual HitWindows CreateHitWindows() => new HitWindows(); protected virtual HitWindows CreateHitWindows() => new HitWindows();
private static int compareObjects(HitObject first, HitObject second) => first.StartTime.CompareTo(second.StartTime);
} }
} }

View File

@ -18,9 +18,14 @@ namespace osu.Game.Rulesets.Timing
public double StartTime; public double StartTime;
/// <summary> /// <summary>
/// The multiplier which this <see cref="MultiplierControlPoint"/> provides. /// The aggregate multiplier which this <see cref="MultiplierControlPoint"/> provides.
/// </summary> /// </summary>
public double Multiplier => 1000 / TimingPoint.BeatLength * DifficultyPoint.SpeedMultiplier; public double Multiplier => Velocity * DifficultyPoint.SpeedMultiplier * 1000 / TimingPoint.BeatLength;
/// <summary>
/// The velocity multiplier.
/// </summary>
public double Velocity = 1;
/// <summary> /// <summary>
/// The <see cref="TimingControlPoint"/> that provides the timing information for this <see cref="MultiplierControlPoint"/>. /// The <see cref="TimingControlPoint"/> that provides the timing information for this <see cref="MultiplierControlPoint"/>.
@ -48,18 +53,6 @@ namespace osu.Game.Rulesets.Timing
StartTime = startTime; StartTime = startTime;
} }
/// <summary>
/// Creates a <see cref="MultiplierControlPoint"/> by copying another <see cref="MultiplierControlPoint"/>.
/// </summary>
/// <param name="startTime">The start time of this <see cref="MultiplierControlPoint"/>.</param>
/// <param name="other">The <see cref="MultiplierControlPoint"/> to copy.</param>
public MultiplierControlPoint(double startTime, MultiplierControlPoint other)
: this(startTime)
{
TimingPoint = other.TimingPoint;
DifficultyPoint = other.DifficultyPoint;
}
// ReSharper disable once ImpureMethodCallOnReadonlyValueField // ReSharper disable once ImpureMethodCallOnReadonlyValueField
public int CompareTo(MultiplierControlPoint other) => StartTime.CompareTo(other?.StartTime); public int CompareTo(MultiplierControlPoint other) => StartTime.CompareTo(other?.StartTime);
} }

View File

@ -14,6 +14,11 @@ namespace osu.Game.Rulesets.UI
public IEnumerable<DrawableHitObject> Objects => InternalChildren.Cast<DrawableHitObject>().OrderBy(h => h.HitObject.StartTime); public IEnumerable<DrawableHitObject> Objects => InternalChildren.Cast<DrawableHitObject>().OrderBy(h => h.HitObject.StartTime);
public IEnumerable<DrawableHitObject> AliveObjects => AliveInternalChildren.Cast<DrawableHitObject>().OrderBy(h => h.HitObject.StartTime); public IEnumerable<DrawableHitObject> AliveObjects => AliveInternalChildren.Cast<DrawableHitObject>().OrderBy(h => h.HitObject.StartTime);
public HitObjectContainer()
{
RelativeSizeAxes = Axes.Both;
}
public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject); public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject);
public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject); public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject);

View File

@ -9,17 +9,26 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using OpenTK;
namespace osu.Game.Rulesets.UI namespace osu.Game.Rulesets.UI
{ {
public abstract class Playfield : ScalableContainer public abstract class Playfield : CompositeDrawable
{ {
/// <summary> /// <summary>
/// The <see cref="DrawableHitObject"/> contained in this Playfield. /// The <see cref="DrawableHitObject"/> contained in this Playfield.
/// </summary> /// </summary>
public HitObjectContainer HitObjectContainer { get; private set; } public HitObjectContainer HitObjectContainer => hitObjectContainerLazy.Value;
private readonly Lazy<HitObjectContainer> hitObjectContainerLazy;
/// <summary>
/// A function that converts gamefield coordinates to screen space.
/// </summary>
public Func<Vector2, Vector2> GamefieldToScreenSpace => HitObjectContainer.ToScreenSpace;
/// <summary> /// <summary>
/// All the <see cref="DrawableHitObject"/>s contained in this <see cref="Playfield"/> and all <see cref="NestedPlayfields"/>. /// All the <see cref="DrawableHitObject"/>s contained in this <see cref="Playfield"/> and all <see cref="NestedPlayfields"/>.
@ -39,18 +48,13 @@ namespace osu.Game.Rulesets.UI
public readonly BindableBool DisplayJudgements = new BindableBool(true); public readonly BindableBool DisplayJudgements = new BindableBool(true);
/// <summary> /// <summary>
/// A container for keeping track of DrawableHitObjects. /// Creates a new <see cref="Playfield"/>.
/// </summary> /// </summary>
/// <param name="customWidth">The width to scale the internal coordinate space to. protected Playfield()
/// May be null if scaling based on <paramref name="customHeight"/> is desired. If <paramref name="customHeight"/> is also null, no scaling will occur.
/// </param>
/// <param name="customHeight">The height to scale the internal coordinate space to.
/// May be null if scaling based on <paramref name="customWidth"/> is desired. If <paramref name="customWidth"/> is also null, no scaling will occur.
/// </param>
protected Playfield(float? customWidth = null, float? customHeight = null)
: base(customWidth, customHeight)
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
hitObjectContainerLazy = new Lazy<HitObjectContainer>(CreateHitObjectContainer);
} }
private WorkingBeatmap beatmap; private WorkingBeatmap beatmap;
@ -59,11 +63,6 @@ namespace osu.Game.Rulesets.UI
private void load(IBindableBeatmap beatmap) private void load(IBindableBeatmap beatmap)
{ {
this.beatmap = beatmap.Value; this.beatmap = beatmap.Value;
HitObjectContainer = CreateHitObjectContainer();
HitObjectContainer.RelativeSizeAxes = Axes.Both;
Add(HitObjectContainer);
} }
/// <summary> /// <summary>
@ -94,10 +93,14 @@ namespace osu.Game.Rulesets.UI
nestedPlayfields.Value.Add(otherPlayfield); nestedPlayfields.Value.Add(otherPlayfield);
} }
/// <summary> protected override void LoadComplete()
/// Creates the container that will be used to contain the <see cref="DrawableHitObject"/>s. {
/// </summary> base.LoadComplete();
protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer();
// in the case a consumer forgets to add the HitObjectContainer, we will add it here.
if (HitObjectContainer.Parent == null)
AddInternal(HitObjectContainer);
}
protected override void Update() protected override void Update()
{ {
@ -108,5 +111,10 @@ namespace osu.Game.Rulesets.UI
if (mod is IUpdatableByPlayfield updatable) if (mod is IUpdatableByPlayfield updatable)
updatable.Update(this); updatable.Update(this);
} }
/// <summary>
/// Creates the container that will be used to contain the <see cref="DrawableHitObject"/>s.
/// </summary>
protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer();
} }
} }

View File

@ -22,7 +22,6 @@ using osu.Game.Overlays;
using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using OpenTK;
namespace osu.Game.Rulesets.UI namespace osu.Game.Rulesets.UI
{ {
@ -316,25 +315,6 @@ namespace osu.Game.Rulesets.UI
Playfield.Add(drawableObject); Playfield.Add(drawableObject);
} }
protected override void Update()
{
base.Update();
Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea;
}
/// <summary>
/// Computes the size of the <see cref="Playfield"/> in relative coordinate space after aspect adjustments.
/// </summary>
/// <returns>The aspect-adjusted size.</returns>
protected virtual Vector2 GetAspectAdjustedSize() => Vector2.One;
/// <summary>
/// The area of this <see cref="RulesetContainer"/> that is available for the <see cref="Playfield"/> to use.
/// Must be specified in relative coordinate space to this <see cref="RulesetContainer"/>.
/// This affects the final size of the <see cref="Playfield"/> but does not affect the <see cref="Playfield"/>'s scale.
/// </summary>
protected virtual Vector2 PlayfieldArea => new Vector2(0.75f); // A sane default
/// <summary> /// <summary>
/// Creates a DrawableHitObject from a HitObject. /// Creates a DrawableHitObject from a HitObject.

View File

@ -73,12 +73,10 @@ namespace osu.Game.Rulesets.UI
#region IHasReplayHandler #region IHasReplayHandler
private ReplayInputHandler replayInputHandler; private ReplayInputHandler replayInputHandler;
public ReplayInputHandler ReplayInputHandler public ReplayInputHandler ReplayInputHandler
{ {
get get => replayInputHandler;
{
return replayInputHandler;
}
set set
{ {
if (replayInputHandler != null) RemoveHandler(replayInputHandler); if (replayInputHandler != null) RemoveHandler(replayInputHandler);
@ -208,16 +206,20 @@ namespace osu.Game.Rulesets.UI
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons); mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
} }
protected override bool OnMouseDown(MouseDownEvent e) protected override bool Handle(UIEvent e)
{ {
if (mouseDisabled.Value && (e.Button == MouseButton.Left || e.Button == MouseButton.Right)) return false; switch (e)
return base.OnMouseDown(e); {
} case MouseDownEvent mouseDown when mouseDown.Button == MouseButton.Left || mouseDown.Button == MouseButton.Right:
if (mouseDisabled.Value)
protected override bool OnMouseUp(MouseUpEvent e) return false;
{ break;
if (!CurrentState.Mouse.IsPressed(e.Button)) return false; case MouseUpEvent mouseUp:
return base.OnMouseUp(e); if (!CurrentState.Mouse.IsPressed(mouseUp.Button))
return false;
break;
}
return base.Handle(e);
} }
#endregion #endregion
@ -269,7 +271,7 @@ namespace osu.Game.Rulesets.UI
} }
public class RulesetInputManagerInputState<T> : InputState public class RulesetInputManagerInputState<T> : InputState
where T : struct where T : struct
{ {
public ReplayState<T> LastReplayState; public ReplayState<T> LastReplayState;

View File

@ -1,99 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
namespace osu.Game.Rulesets.UI
{
/// <summary>
/// A <see cref="Container"/> which can have its internal coordinate system scaled to a specific size.
/// </summary>
public class ScalableContainer : Container
{
/// <summary>
/// A function that converts coordinates from gamefield to screen space.
/// </summary>
public Func<Vector2, Vector2> GamefieldToScreenSpace => scaledContent.GamefieldToScreenSpace;
/// <summary>
/// The scaled content.
/// </summary>
private readonly ScaledContainer scaledContent;
protected override Container<Drawable> Content => scaledContent;
/// <summary>
/// A <see cref="Container"/> which can have its internal coordinate system scaled to a specific size.
/// </summary>
/// <param name="customWidth">The width to scale the internal coordinate space to.
/// May be null if scaling based on <paramref name="customHeight"/> is desired. If <paramref name="customHeight"/> is also null, no scaling will occur.
/// </param>
/// <param name="customHeight">The height to scale the internal coordinate space to.
/// May be null if scaling based on <paramref name="customWidth"/> is desired. If <paramref name="customWidth"/> is also null, no scaling will occur.
/// </param>
public ScalableContainer(float? customWidth = null, float? customHeight = null)
{
AddInternal(scaledContent = new ScaledContainer
{
CustomWidth = customWidth,
CustomHeight = customHeight,
RelativeSizeAxes = Axes.Both,
});
}
private class ScaledContainer : Container
{
/// <summary>
/// A function that converts coordinates from gamefield to screen space.
/// </summary>
public Func<Vector2, Vector2> GamefieldToScreenSpace => content.ToScreenSpace;
/// <summary>
/// The value to scale the width of the content to match.
/// If null, <see cref="CustomHeight"/> is used.
/// </summary>
public float? CustomWidth;
/// <summary>
/// The value to scale the height of the content to match.
/// if null, <see cref="CustomWidth"/> is used.
/// </summary>
public float? CustomHeight;
private readonly Container content;
protected override Container<Drawable> Content => content;
public ScaledContainer()
{
AddInternal(content = new Container { RelativeSizeAxes = Axes.Both });
}
protected override void Update()
{
base.Update();
content.Scale = sizeScale;
content.Size = Vector2.Divide(Vector2.One, sizeScale);
}
/// <summary>
/// The scale that is required for the size of the content to match <see cref="CustomWidth"/> and <see cref="CustomHeight"/>.
/// </summary>
private Vector2 sizeScale
{
get
{
if (CustomWidth.HasValue && CustomHeight.HasValue)
return Vector2.Divide(DrawSize, new Vector2(CustomWidth.Value, CustomHeight.Value));
if (CustomWidth.HasValue)
return new Vector2(DrawSize.X / CustomWidth.Value);
if (CustomHeight.HasValue)
return new Vector2(DrawSize.Y / CustomHeight.Value);
return Vector2.One;
}
}
}
}
}

View File

@ -6,19 +6,19 @@ namespace osu.Game.Rulesets.UI.Scrolling
public enum ScrollingDirection public enum ScrollingDirection
{ {
/// <summary> /// <summary>
/// Hitobjects will scroll vertically from the bottom of the hitobject container. /// Hit objects will scroll vertically from the bottom of the hitobject container.
/// </summary> /// </summary>
Up, Up,
/// <summary> /// <summary>
/// Hitobjects will scroll vertically from the top of the hitobject container. /// Hit objects will scroll vertically from the top of the hitobject container.
/// </summary> /// </summary>
Down, Down,
/// <summary> /// <summary>
/// Hitobjects will scroll horizontally from the right of the hitobject container. /// Hit objects will scroll horizontally from the right of the hitobject container.
/// </summary> /// </summary>
Left, Left,
/// <summary> /// <summary>
/// Hitobjects will scroll horizontally from the left of the hitobject container. /// Hit objects will scroll horizontally from the left of the hitobject container.
/// </summary> /// </summary>
Right Right
} }

View File

@ -65,20 +65,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
protected virtual SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Sequential; protected virtual SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Sequential;
/// <summary>
/// Creates a new <see cref="ScrollingPlayfield"/>.
/// </summary>
/// <param name="customWidth">The width to scale the internal coordinate space to.
/// May be null if scaling based on <paramref name="customHeight"/> is desired. If <paramref name="customHeight"/> is also null, no scaling will occur.
/// </param>
/// <param name="customHeight">The height to scale the internal coordinate space to.
/// May be null if scaling based on <paramref name="customWidth"/> is desired. If <paramref name="customWidth"/> is also null, no scaling will occur.
/// </param>
protected ScrollingPlayfield(float? customWidth = null, float? customHeight = null)
: base(customWidth, customHeight)
{
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {

View File

@ -60,6 +60,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
return new MultiplierControlPoint(c.Time) return new MultiplierControlPoint(c.Time)
{ {
Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier,
TimingPoint = lastTimingPoint, TimingPoint = lastTimingPoint,
DifficultyPoint = lastDifficultyPoint DifficultyPoint = lastDifficultyPoint
}; };
@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
// If we have no control points, add a default one // If we have no control points, add a default one
if (DefaultControlPoints.Count == 0) if (DefaultControlPoints.Count == 0)
DefaultControlPoints.Add(new MultiplierControlPoint()); DefaultControlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier });
DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield)); DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
} }
@ -88,22 +89,5 @@ namespace osu.Game.Rulesets.UI.Scrolling
playfield.HitObjects.AddControlPoint(controlPoint); playfield.HitObjects.AddControlPoint(controlPoint);
playfield.NestedPlayfields?.OfType<ScrollingPlayfield>().ForEach(p => applySpeedAdjustment(controlPoint, p)); playfield.NestedPlayfields?.OfType<ScrollingPlayfield>().ForEach(p => applySpeedAdjustment(controlPoint, p));
} }
/// <summary>
/// Generates a <see cref="MultiplierControlPoint"/> with the default timing change/difficulty change from the beatmap at a time.
/// </summary>
/// <param name="time">The time to create the control point at.</param>
/// <returns>The default <see cref="MultiplierControlPoint"/> at <paramref name="time"/>.</returns>
public MultiplierControlPoint CreateControlPointAt(double time)
{
if (DefaultControlPoints.Count == 0)
return new MultiplierControlPoint(time);
int index = DefaultControlPoints.BinarySearch(new MultiplierControlPoint(time));
if (index < 0)
return new MultiplierControlPoint(time);
return new MultiplierControlPoint(time, DefaultControlPoints[index]);
}
} }
} }

View File

@ -44,7 +44,7 @@ namespace osu.Game.Screens.Edit.Components
new OsuSpriteText new OsuSpriteText
{ {
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Text = "Playback Speed", Text = "Playback speed",
RelativePositionAxes = Axes.Y, RelativePositionAxes = Axes.Y,
Y = 0.5f, Y = 0.5f,
Padding = new MarginPadding { Left = 45 } Padding = new MarginPadding { Left = 45 }

View File

@ -59,8 +59,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
Spacing = new Vector2(0, 4), Spacing = new Vector2(0, 4),
Children = new[] Children = new[]
{ {
hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hitobjects" }, hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hit objects" },
hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hitsounds" }, hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hit sounds" },
waveformCheckbox = new OsuCheckbox { LabelText = "Waveform" } waveformCheckbox = new OsuCheckbox { LabelText = "Waveform" }
} }
} }

View File

@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
}, },
showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" }, showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" },
beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" }, beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" },
beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" } beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hit sounds" }
}; };
} }

View File

@ -327,11 +327,6 @@ namespace osu.Game.Screens.Select
TextSize = 14, TextSize = 14,
}, },
}, },
textFlow = new OsuTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
}, },
}; };
} }
@ -350,6 +345,8 @@ namespace osu.Game.Screens.Select
} }
} }
public override bool IsPresent => base.IsPresent || textFlow == null; // Visibility is updated in the LoadComponentAsync callback
private void setTextAsync(string text) private void setTextAsync(string text)
{ {
LoadComponentAsync(new OsuTextFlowContainer(s => s.TextSize = 14) LoadComponentAsync(new OsuTextFlowContainer(s => s.TextSize = 14)

View File

@ -21,8 +21,8 @@ namespace osu.Game.Screens.Select.Filter
DateAdded, DateAdded,
[Description("Difficulty")] [Description("Difficulty")]
Difficulty, Difficulty,
[Description("Favorites")] [Description("Favourites")]
Favorites, Favourites,
[Description("Length")] [Description("Length")]
Length, Length,
[Description("My Maps")] [Description("My Maps")]

View File

@ -65,7 +65,7 @@ namespace osu.Game.Screens.Select
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1); BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2); BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2);
BeatmapOptions.AddButton(@"Edit", @"Beatmap", FontAwesome.fa_pencil, colours.Yellow, () => BeatmapOptions.AddButton(@"Edit", @"beatmap", FontAwesome.fa_pencil, colours.Yellow, () =>
{ {
ValidForResume = false; ValidForResume = false;
Push(new Editor()); Push(new Editor());

View File

@ -204,7 +204,7 @@ namespace osu.Game.Screens.Select
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2); Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
Footer.AddButton(@"options", colours.Blue, BeatmapOptions, Key.F3); Footer.AddButton(@"options", colours.Blue, BeatmapOptions, Key.F3);
BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue); BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
} }
if (this.beatmaps == null) if (this.beatmaps == null)

View File

@ -41,7 +41,7 @@ namespace osu.Game.Storyboards.Drawables
{ {
base.Update(); base.Update();
// TODO: this logic will need to be consolidated with other game samples like hitsounds. // TODO: this logic will need to be consolidated with other game samples like hit sounds.
if (Time.Current < sample.Time) if (Time.Current < sample.Time)
{ {
// We've rewound before the start time of the sample // We've rewound before the start time of the sample

View File

@ -83,8 +83,8 @@ namespace osu.Game.Users
[JsonProperty(@"location")] [JsonProperty(@"location")]
public string Location; public string Location;
[JsonProperty(@"lastvisit")] [JsonProperty(@"last_visit")]
public DateTimeOffset LastVisit; public DateTimeOffset? LastVisit;
[JsonProperty(@"twitter")] [JsonProperty(@"twitter")]
public string Twitter; public string Twitter;

View File

@ -14,13 +14,13 @@
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" /> <ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Humanizer" Version="2.4.2" /> <PackageReference Include="Humanizer" Version="2.5.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.4" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="ppy.osu.Framework" Version="2018.1002.0" /> <PackageReference Include="ppy.osu.Framework" Version="2018.1012.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.10.1" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
</ItemGroup> </ItemGroup>

View File

@ -10,10 +10,6 @@
<ProjectReference Include="..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" /> <ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
<PackageReference Include="DeepEqual" Version="1.6.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\osu.Game\Tests\VisualTestRunner.cs"> <Compile Include="..\osu.Game\Tests\VisualTestRunner.cs">
<Link>VisualTestRunner.cs</Link> <Link>VisualTestRunner.cs</Link>

View File

@ -666,6 +666,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>