mirror of
https://github.com/osukey/osukey.git
synced 2025-04-29 02:37:25 +09:00
Merge branch 'master' into chat-report
This commit is contained in:
commit
4f55afb60d
@ -105,7 +105,7 @@ When it comes to contributing to the project, the two main things you can do to
|
|||||||
|
|
||||||
If you wish to help with localisation efforts, head over to [crowdin](https://crowdin.com/project/osu-web).
|
If you wish to help with localisation efforts, head over to [crowdin](https://crowdin.com/project/osu-web).
|
||||||
|
|
||||||
For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via PayPal or osu!supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project.
|
We love to reward quality contributions. If you have made a large contribution, or are a regular contributor, you are welcome to [submit an expense via opencollective](https://opencollective.com/ppy/expenses/new). If you have any questions, feel free to [reach out to peppy](mailto:pe@ppy.sh) before doing so.
|
||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
|
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.327.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.418.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />
|
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1]));
|
AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1]));
|
||||||
AddAssert("start position is correct", () => Precision.AlmostEquals(lastObject.OriginalX, positions[0]));
|
AddAssert("start position is correct", () => Precision.AlmostEquals(lastObject.OriginalX, positions[0]));
|
||||||
AddAssert("end position is correct", () => Precision.AlmostEquals(lastObject.EndX, positions[1]));
|
AddAssert("end position is correct", () => Precision.AlmostEquals(lastObject.EndX, positions[1]));
|
||||||
AddAssert("default slider velocity", () => lastObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault);
|
AddAssert("default slider velocity", () => lastObject.SliderVelocityBindable.IsDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
addPlacementSteps(times, positions);
|
addPlacementSteps(times, positions);
|
||||||
addPathCheckStep(times, positions);
|
addPathCheckStep(times, positions);
|
||||||
|
|
||||||
AddAssert("slider velocity changed", () => !lastObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault);
|
AddAssert("slider velocity changed", () => !lastObject.SliderVelocityBindable.IsDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -108,11 +108,11 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
double[] times = { 100, 300 };
|
double[] times = { 100, 300 };
|
||||||
float[] positions = { 200, 300 };
|
float[] positions = { 200, 300 };
|
||||||
addBlueprintStep(times, positions);
|
addBlueprintStep(times, positions);
|
||||||
AddAssert("default slider velocity", () => hitObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault);
|
AddAssert("default slider velocity", () => hitObject.SliderVelocityBindable.IsDefault);
|
||||||
|
|
||||||
addDragStartStep(times[1], positions[1]);
|
addDragStartStep(times[1], positions[1]);
|
||||||
AddMouseMoveStep(times[1], 400);
|
AddMouseMoveStep(times[1], 400);
|
||||||
AddAssert("slider velocity changed", () => !hitObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault);
|
AddAssert("slider velocity changed", () => !hitObject.SliderVelocityBindable.IsDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -26,6 +26,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
var xPositionData = obj as IHasXPosition;
|
var xPositionData = obj as IHasXPosition;
|
||||||
var yPositionData = obj as IHasYPosition;
|
var yPositionData = obj as IHasYPosition;
|
||||||
var comboData = obj as IHasCombo;
|
var comboData = obj as IHasCombo;
|
||||||
|
var sliderVelocityData = obj as IHasSliderVelocity;
|
||||||
|
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
@ -41,7 +42,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||||
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0,
|
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0,
|
||||||
LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y
|
LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y,
|
||||||
|
SliderVelocity = sliderVelocityData?.SliderVelocity ?? 1
|
||||||
}.Yield();
|
}.Yield();
|
||||||
|
|
||||||
case IHasDuration endTime:
|
case IHasDuration endTime:
|
||||||
|
@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
public void UpdateHitObjectFromPath(JuiceStream hitObject)
|
public void UpdateHitObjectFromPath(JuiceStream hitObject)
|
||||||
{
|
{
|
||||||
// The SV setting may need to be changed for the current path.
|
// The SV setting may need to be changed for the current path.
|
||||||
var svBindable = hitObject.DifficultyControlPoint.SliderVelocityBindable;
|
var svBindable = hitObject.SliderVelocityBindable;
|
||||||
double svToVelocityFactor = hitObject.Velocity / svBindable.Value;
|
double svToVelocityFactor = hitObject.Velocity / svBindable.Value;
|
||||||
double requiredVelocity = path.ComputeRequiredVelocity();
|
double requiredVelocity = path.ComputeRequiredVelocity();
|
||||||
|
|
||||||
|
@ -48,7 +48,6 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
// todo: enable distance spacing once catch supports applying it to its existing distance snap grid implementation.
|
// todo: enable distance spacing once catch supports applying it to its existing distance snap grid implementation.
|
||||||
RightSideToolboxContainer.Alpha = 0;
|
|
||||||
DistanceSpacingMultiplier.Disabled = true;
|
DistanceSpacingMultiplier.Disabled = true;
|
||||||
|
|
||||||
LayerBelowRuleset.Add(new PlayfieldBorder
|
LayerBelowRuleset.Add(new PlayfieldBorder
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModDaycore : ModDaycore
|
public class CatchModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
public DifficultyBindable CircleSize { get; } = new DifficultyBindable
|
public DifficultyBindable CircleSize { get; } = new DifficultyBindable
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
Precision = 0.1f,
|
||||||
MinValue = 1,
|
MinValue = 0,
|
||||||
MaxValue = 10,
|
MaxValue = 10,
|
||||||
ExtendedMaxValue = 11,
|
ExtendedMaxValue = 11,
|
||||||
ReadCurrentFromDifficulty = diff => diff.CircleSize,
|
ReadCurrentFromDifficulty = diff => diff.CircleSize,
|
||||||
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
public DifficultyBindable ApproachRate { get; } = new DifficultyBindable
|
public DifficultyBindable ApproachRate { get; } = new DifficultyBindable
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
Precision = 0.1f,
|
||||||
MinValue = 1,
|
MinValue = 0,
|
||||||
MaxValue = 10,
|
MaxValue = 10,
|
||||||
ExtendedMaxValue = 11,
|
ExtendedMaxValue = 11,
|
||||||
ReadCurrentFromDifficulty = diff => diff.ApproachRate,
|
ReadCurrentFromDifficulty = diff => diff.ApproachRate,
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModDoubleTime : ModDoubleTime
|
public class CatchModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModHalfTime : ModHalfTime
|
public class CatchModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModNightcore : ModNightcore<CatchHitObject>
|
public class CatchModNightcore : ModNightcore<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public override Judgement CreateJudgement() => new CatchBananaJudgement();
|
public override Judgement CreateJudgement() => new CatchBananaJudgement();
|
||||||
|
|
||||||
private static readonly List<HitSampleInfo> samples = new List<HitSampleInfo> { new BananaHitSampleInfo() };
|
private static readonly IList<HitSampleInfo> default_banana_samples = new List<HitSampleInfo> { new BananaHitSampleInfo() }.AsReadOnly();
|
||||||
|
|
||||||
public Banana()
|
public Banana()
|
||||||
{
|
{
|
||||||
Samples = samples;
|
Samples = default_banana_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
// override any external colour changes with banananana
|
// override any external colour changes with banananana
|
||||||
@ -47,13 +47,13 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BananaHitSampleInfo : HitSampleInfo, IEquatable<BananaHitSampleInfo>
|
public class BananaHitSampleInfo : HitSampleInfo, IEquatable<BananaHitSampleInfo>
|
||||||
{
|
{
|
||||||
private static readonly string[] lookup_names = { "Gameplay/metronomelow", "Gameplay/catch-banana" };
|
private static readonly string[] lookup_names = { "Gameplay/metronomelow", "Gameplay/catch-banana" };
|
||||||
|
|
||||||
public override IEnumerable<string> LookupNames => lookup_names;
|
public override IEnumerable<string> LookupNames => lookup_names;
|
||||||
|
|
||||||
public BananaHitSampleInfo(int volume = 0)
|
public BananaHitSampleInfo(int volume = 100)
|
||||||
: base(string.Empty, volume: volume)
|
: base(string.Empty, volume: volume)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
@ -39,6 +41,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
StartTime = time,
|
StartTime = time,
|
||||||
BananaIndex = i,
|
BananaIndex = i,
|
||||||
|
Samples = new List<HitSampleInfo> { new Banana.BananaHitSampleInfo(GetSampleInfo().Volume) }
|
||||||
});
|
});
|
||||||
|
|
||||||
time += spacing;
|
time += spacing;
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -16,7 +17,7 @@ using osu.Game.Rulesets.Objects.Types;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class JuiceStream : CatchHitObject, IHasPathWithRepeats
|
public class JuiceStream : CatchHitObject, IHasPathWithRepeats, IHasSliderVelocity
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
||||||
@ -27,6 +28,19 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public int RepeatCount { get; set; }
|
public int RepeatCount { get; set; }
|
||||||
|
|
||||||
|
public BindableNumber<double> SliderVelocityBindable { get; } = new BindableDouble(1)
|
||||||
|
{
|
||||||
|
Precision = 0.01,
|
||||||
|
MinValue = 0.1,
|
||||||
|
MaxValue = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
public double SliderVelocity
|
||||||
|
{
|
||||||
|
get => SliderVelocityBindable.Value;
|
||||||
|
set => SliderVelocityBindable.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
private double velocityFactor;
|
private double velocityFactor;
|
||||||
|
|
||||||
@ -34,10 +48,10 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
private double tickDistanceFactor;
|
private double tickDistanceFactor;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public double Velocity => velocityFactor * DifficultyControlPoint.SliderVelocity;
|
public double Velocity => velocityFactor * SliderVelocity;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public double TickDistance => tickDistanceFactor * DifficultyControlPoint.SliderVelocity;
|
public double TickDistance => tickDistanceFactor * SliderVelocity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The length of one span of this <see cref="JuiceStream"/>.
|
/// The length of one span of this <see cref="JuiceStream"/>.
|
||||||
|
@ -136,6 +136,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
Origin = Anchor.TopCentre;
|
Origin = Anchor.TopCentre;
|
||||||
|
|
||||||
Size = new Vector2(BASE_SIZE);
|
Size = new Vector2(BASE_SIZE);
|
||||||
|
|
||||||
if (difficulty != null)
|
if (difficulty != null)
|
||||||
Scale = calculateScale(difficulty);
|
Scale = calculateScale(difficulty);
|
||||||
|
|
||||||
@ -333,8 +334,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
var scaleFromDirection = new Vector2((int)VisualDirection, 1);
|
var scaleFromDirection = new Vector2((int)VisualDirection, 1);
|
||||||
|
|
||||||
body.Scale = scaleFromDirection;
|
body.Scale = scaleFromDirection;
|
||||||
caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
|
// Inverse of catcher scale is applied here, as catcher gets scaled by circle size and so do the incoming fruit.
|
||||||
|
caughtObjectContainer.Scale = (1 / Scale.X) * (flipCatcherPlate ? scaleFromDirection : Vector2.One);
|
||||||
|
hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
|
||||||
|
|
||||||
// Correct overshooting.
|
// Correct overshooting.
|
||||||
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||||
@ -414,10 +418,13 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
private void clearPlate(DroppedObjectAnimation animation)
|
private void clearPlate(DroppedObjectAnimation animation)
|
||||||
{
|
{
|
||||||
var droppedObjects = caughtObjectContainer.Children.Select(getDroppedObject).ToArray();
|
var caughtObjects = caughtObjectContainer.Children.ToArray();
|
||||||
|
|
||||||
caughtObjectContainer.Clear(false);
|
caughtObjectContainer.Clear(false);
|
||||||
|
|
||||||
|
// Use the already returned PoolableDrawables for new objects
|
||||||
|
var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray();
|
||||||
|
|
||||||
droppedObjectTarget.AddRange(droppedObjects);
|
droppedObjectTarget.AddRange(droppedObjects);
|
||||||
|
|
||||||
foreach (var droppedObject in droppedObjects)
|
foreach (var droppedObject in droppedObjects)
|
||||||
@ -426,10 +433,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation)
|
private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation)
|
||||||
{
|
{
|
||||||
var droppedObject = getDroppedObject(caughtObject);
|
|
||||||
|
|
||||||
caughtObjectContainer.Remove(caughtObject, false);
|
caughtObjectContainer.Remove(caughtObject, false);
|
||||||
|
|
||||||
|
var droppedObject = getDroppedObject(caughtObject);
|
||||||
|
|
||||||
droppedObjectTarget.Add(droppedObject);
|
droppedObjectTarget.Add(droppedObject);
|
||||||
|
|
||||||
applyDropAnimation(droppedObject, animation);
|
applyDropAnimation(droppedObject, animation);
|
||||||
@ -452,6 +459,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define lifetime start for dropped objects to be disposed correctly when rewinding replay
|
||||||
|
d.LifetimeStart = Clock.CurrentTime;
|
||||||
d.Expire();
|
d.Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Formats;
|
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
@ -49,15 +48,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
Debug.Assert(distanceData != null);
|
Debug.Assert(distanceData != null);
|
||||||
|
|
||||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||||
DifficultyControlPoint difficultyPoint = hitObject.DifficultyControlPoint;
|
|
||||||
|
|
||||||
double beatLength;
|
double beatLength;
|
||||||
#pragma warning disable 618
|
if (hitObject.LegacyBpmMultiplier.HasValue)
|
||||||
if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint)
|
beatLength = timingPoint.BeatLength * hitObject.LegacyBpmMultiplier.Value;
|
||||||
#pragma warning restore 618
|
else if (hitObject is IHasSliderVelocity hasSliderVelocity)
|
||||||
beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier;
|
beatLength = timingPoint.BeatLength / hasSliderVelocity.SliderVelocity;
|
||||||
else
|
else
|
||||||
beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity;
|
beatLength = timingPoint.BeatLength;
|
||||||
|
|
||||||
SpanCount = repeatsData?.SpanCount() ?? 1;
|
SpanCount = repeatsData?.SpanCount() ?? 1;
|
||||||
StartTime = (int)Math.Round(hitObject.StartTime);
|
StartTime = (int)Math.Round(hitObject.StartTime);
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModDaycore : ModDaycore
|
public class ManiaModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.5;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModDoubleTime : ModDoubleTime
|
public class ManiaModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModHalfTime : ModHalfTime
|
public class ManiaModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.5;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModNightcore : ModNightcore<ManiaHitObject>
|
public class ManiaModNightcore : ModNightcore<ManiaHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -350,13 +350,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
// Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being.
|
// Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being.
|
||||||
|
|
||||||
if (HitObject.SampleControlPoint == null)
|
slidingSample.Samples = HitObject.CreateSlidingSamples().Cast<ISampleInfo>().ToArray();
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
|
||||||
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
slidingSample.Samples = HitObject.CreateSlidingSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void StopAllSamples()
|
public override void StopAllSamples()
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
["Gameplay/soft-hitnormal"],
|
["Gameplay/soft-hitnormal"],
|
||||||
["Gameplay/drum-hitnormal"]
|
["Gameplay/drum-hitnormal"]
|
||||||
],
|
],
|
||||||
"Samples": ["Gameplay/-hitnormal"]
|
"Samples": ["Gameplay/normal-hitnormal"]
|
||||||
}, {
|
}, {
|
||||||
"StartTime": 1875.0,
|
"StartTime": 1875.0,
|
||||||
"EndTime": 2750.0,
|
"EndTime": 2750.0,
|
||||||
@ -19,7 +19,7 @@
|
|||||||
["Gameplay/soft-hitnormal"],
|
["Gameplay/soft-hitnormal"],
|
||||||
["Gameplay/drum-hitnormal"]
|
["Gameplay/drum-hitnormal"]
|
||||||
],
|
],
|
||||||
"Samples": ["Gameplay/-hitnormal"]
|
"Samples": ["Gameplay/normal-hitnormal"]
|
||||||
}]
|
}]
|
||||||
}, {
|
}, {
|
||||||
"StartTime": 3750.0,
|
"StartTime": 3750.0,
|
||||||
|
@ -138,8 +138,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First();
|
var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First();
|
||||||
return slider1 is not null && mergedSlider.HeadCircle.Samples.SequenceEqual(slider1.HeadCircle.Samples)
|
return slider1 is not null && mergedSlider.HeadCircle.Samples.SequenceEqual(slider1.HeadCircle.Samples)
|
||||||
&& mergedSlider.TailCircle.Samples.SequenceEqual(slider1.TailCircle.Samples)
|
&& mergedSlider.TailCircle.Samples.SequenceEqual(slider1.TailCircle.Samples)
|
||||||
&& mergedSlider.Samples.SequenceEqual(slider1.Samples)
|
&& mergedSlider.Samples.SequenceEqual(slider1.Samples);
|
||||||
&& mergedSlider.SampleControlPoint.IsRedundant(slider1.SampleControlPoint);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("slider end is at same completion for last slider", () =>
|
AddAssert("slider end is at same completion for last slider", () =>
|
||||||
|
@ -181,10 +181,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
if (slider is null) return;
|
if (slider is null) return;
|
||||||
|
|
||||||
slider.SampleControlPoint.SampleBank = "soft";
|
sample = new HitSampleInfo("hitwhistle", "soft", volume: 70);
|
||||||
slider.SampleControlPoint.SampleVolume = 70;
|
slider.Samples.Add(sample.With());
|
||||||
sample = new HitSampleInfo("hitwhistle");
|
|
||||||
slider.Samples.Add(sample);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("select added slider", () =>
|
AddStep("select added slider", () =>
|
||||||
@ -207,9 +205,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddAssert("sliders have hitsounds", hasHitsounds);
|
AddAssert("sliders have hitsounds", hasHitsounds);
|
||||||
|
|
||||||
bool hasHitsounds() => sample is not null &&
|
bool hasHitsounds() => sample is not null &&
|
||||||
EditorBeatmap.HitObjects.All(o => o.SampleControlPoint.SampleBank == "soft" &&
|
EditorBeatmap.HitObjects.All(o => o.Samples.Contains(sample));
|
||||||
o.SampleControlPoint.SampleVolume == 70 &&
|
|
||||||
o.Samples.Contains(sample));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool sliderCreatedFor(Slider s, double startTime, double endTime, params (Vector2 pos, PathType? pathType)[] expectedControlPoints)
|
private bool sliderCreatedFor(Slider s, double startTime, double endTime, params (Vector2 pos, PathType? pathType)[] expectedControlPoints)
|
||||||
|
@ -199,8 +199,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
Precision.AlmostEquals(circle.StartTime, time, 1)
|
Precision.AlmostEquals(circle.StartTime, time, 1)
|
||||||
&& Precision.AlmostEquals(circle.Position, position, 0.01f)
|
&& Precision.AlmostEquals(circle.Position, position, 0.01f)
|
||||||
&& circle.NewCombo == startsNewCombo
|
&& circle.NewCombo == startsNewCombo
|
||||||
&& circle.Samples.SequenceEqual(slider.HeadCircle.Samples)
|
&& circle.Samples.SequenceEqual(slider.HeadCircle.Samples);
|
||||||
&& circle.SampleControlPoint.IsRedundant(slider.SampleControlPoint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool sliderRestored(Slider slider)
|
private bool sliderRestored(Slider slider)
|
||||||
|
19
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs
Normal file
19
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||||
|
{
|
||||||
|
public partial class TestSceneOsuModBubbles : OsuModTestScene
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestOsuModBubbles() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModBubbles(),
|
||||||
|
Autoplay = true,
|
||||||
|
PassCondition = () => true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
@ -8,6 +8,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -18,6 +19,7 @@ using osu.Framework.Testing.Input;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.Skinning;
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -40,6 +42,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private Drawable background;
|
private Drawable background;
|
||||||
|
|
||||||
|
private readonly Bindable<bool> ripples = new Bindable<bool>();
|
||||||
|
|
||||||
public TestSceneGameplayCursor()
|
public TestSceneGameplayCursor()
|
||||||
{
|
{
|
||||||
var ruleset = new OsuRuleset();
|
var ruleset = new OsuRuleset();
|
||||||
@ -57,6 +61,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddToggleStep("ripples", v => ripples.Value = v);
|
||||||
|
|
||||||
AddSliderStep("circle size", 0f, 10f, 0f, val =>
|
AddSliderStep("circle size", 0f, 10f, 0f, val =>
|
||||||
{
|
{
|
||||||
config.SetValue(OsuSetting.AutoCursorSize, true);
|
config.SetValue(OsuSetting.AutoCursorSize, true);
|
||||||
@ -67,6 +73,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddStep("test cursor container", () => loadContent(false));
|
AddStep("test cursor container", () => loadContent(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
var rulesetConfig = (OsuRulesetConfigManager)RulesetConfigs.GetConfigFor(Ruleset.Value.CreateInstance()).AsNonNull();
|
||||||
|
rulesetConfig.BindWith(OsuRulesetSetting.ShowCursorRipples, ripples);
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase(1, 1)]
|
[TestCase(1, 1)]
|
||||||
[TestCase(5, 1)]
|
[TestCase(5, 1)]
|
||||||
[TestCase(10, 1)]
|
[TestCase(10, 1)]
|
||||||
|
@ -439,7 +439,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public TestSlider()
|
public TestSlider()
|
||||||
{
|
{
|
||||||
DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = 0.1f };
|
SliderVelocity = 0.1f;
|
||||||
|
|
||||||
DefaultsApplied += _ =>
|
DefaultsApplied += _ =>
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,7 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuConfigManager config { get; set; } = null!;
|
private OsuConfigManager config { get; set; } = null!;
|
||||||
|
|
||||||
private TestActionKeyCounter leftKeyCounter = null!;
|
private DefaultKeyCounter leftKeyCounter = null!;
|
||||||
|
|
||||||
private TestActionKeyCounter rightKeyCounter = null!;
|
private DefaultKeyCounter rightKeyCounter = null!;
|
||||||
|
|
||||||
private OsuInputManager osuInputManager = null!;
|
private OsuInputManager osuInputManager = null!;
|
||||||
|
|
||||||
@ -59,14 +59,14 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
leftKeyCounter = new TestActionKeyCounter(OsuAction.LeftButton)
|
leftKeyCounter = new DefaultKeyCounter(new TestActionKeyCounterTrigger(OsuAction.LeftButton))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Depth = float.MinValue,
|
Depth = float.MinValue,
|
||||||
X = -100,
|
X = -100,
|
||||||
},
|
},
|
||||||
rightKeyCounter = new TestActionKeyCounter(OsuAction.RightButton)
|
rightKeyCounter = new DefaultKeyCounter(new TestActionKeyCounterTrigger(OsuAction.RightButton))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
@ -598,8 +598,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private void assertKeyCounter(int left, int right)
|
private void assertKeyCounter(int left, int right)
|
||||||
{
|
{
|
||||||
AddAssert($"The left key was pressed {left} times", () => leftKeyCounter.CountPresses, () => Is.EqualTo(left));
|
AddAssert($"The left key was pressed {left} times", () => leftKeyCounter.CountPresses.Value, () => Is.EqualTo(left));
|
||||||
AddAssert($"The right key was pressed {right} times", () => rightKeyCounter.CountPresses, () => Is.EqualTo(right));
|
AddAssert($"The right key was pressed {right} times", () => rightKeyCounter.CountPresses.Value, () => Is.EqualTo(right));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releaseAllTouches()
|
private void releaseAllTouches()
|
||||||
@ -615,11 +615,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
private void checkNotPressed(OsuAction action) => AddAssert($"Not pressing {action}", () => !osuInputManager.PressedActions.Contains(action));
|
private void checkNotPressed(OsuAction action) => AddAssert($"Not pressing {action}", () => !osuInputManager.PressedActions.Contains(action));
|
||||||
private void checkPressed(OsuAction action) => AddAssert($"Is pressing {action}", () => osuInputManager.PressedActions.Contains(action));
|
private void checkPressed(OsuAction action) => AddAssert($"Is pressing {action}", () => osuInputManager.PressedActions.Contains(action));
|
||||||
|
|
||||||
public partial class TestActionKeyCounter : KeyCounter, IKeyBindingHandler<OsuAction>
|
public partial class TestActionKeyCounterTrigger : InputTrigger, IKeyBindingHandler<OsuAction>
|
||||||
{
|
{
|
||||||
public OsuAction Action { get; }
|
public OsuAction Action { get; }
|
||||||
|
|
||||||
public TestActionKeyCounter(OsuAction action)
|
public TestActionKeyCounterTrigger(OsuAction action)
|
||||||
: base(action.ToString())
|
: base(action.ToString())
|
||||||
{
|
{
|
||||||
Action = action;
|
Action = action;
|
||||||
@ -629,8 +629,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
if (e.Action == Action)
|
if (e.Action == Action)
|
||||||
{
|
{
|
||||||
IsLit = true;
|
Activate();
|
||||||
Increment();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -638,7 +637,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||||
{
|
{
|
||||||
if (e.Action == Action) IsLit = false;
|
if (e.Action == Action)
|
||||||
|
Deactivate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -47,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = time_slider_start,
|
StartTime = time_slider_start,
|
||||||
Position = new Vector2(0, 0),
|
Position = new Vector2(0, 0),
|
||||||
DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = velocity },
|
SliderVelocity = velocity,
|
||||||
Path = new SliderPath(PathType.Linear, new[]
|
Path = new SliderPath(PathType.Linear, new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
|
@ -8,7 +8,6 @@ using System.Linq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -350,7 +349,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = time_slider_start,
|
StartTime = time_slider_start,
|
||||||
Position = new Vector2(0, 0),
|
Position = new Vector2(0, 0),
|
||||||
DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = 0.1f },
|
SliderVelocity = 0.1f,
|
||||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
|
@ -399,7 +399,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public TestSlider()
|
public TestSlider()
|
||||||
{
|
{
|
||||||
DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = 0.1f };
|
SliderVelocity = 0.1f;
|
||||||
|
|
||||||
DefaultsApplied += _ =>
|
DefaultsApplied += _ =>
|
||||||
{
|
{
|
||||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
{
|
{
|
||||||
var positionData = original as IHasPosition;
|
var positionData = original as IHasPosition;
|
||||||
var comboData = original as IHasCombo;
|
var comboData = original as IHasCombo;
|
||||||
|
var sliderVelocityData = original as IHasSliderVelocity;
|
||||||
|
var generateTicksData = original as IHasGenerateTicks;
|
||||||
|
|
||||||
switch (original)
|
switch (original)
|
||||||
{
|
{
|
||||||
@ -47,7 +49,9 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset,
|
LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset,
|
||||||
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
||||||
// this results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
// this results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
||||||
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / ((LegacyControlPointInfo)beatmap.ControlPointInfo).DifficultyPointAt(original.StartTime).SliderVelocity : 1
|
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / ((LegacyControlPointInfo)beatmap.ControlPointInfo).DifficultyPointAt(original.StartTime).SliderVelocity : 1,
|
||||||
|
GenerateTicks = generateTicksData?.GenerateTicks ?? true,
|
||||||
|
SliderVelocity = sliderVelocityData?.SliderVelocity ?? 1,
|
||||||
}.Yield();
|
}.Yield();
|
||||||
|
|
||||||
case IHasDuration endTimeData:
|
case IHasDuration endTimeData:
|
||||||
|
@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Configuration
|
|||||||
SetDefault(OsuRulesetSetting.SnakingInSliders, true);
|
SetDefault(OsuRulesetSetting.SnakingInSliders, true);
|
||||||
SetDefault(OsuRulesetSetting.SnakingOutSliders, true);
|
SetDefault(OsuRulesetSetting.SnakingOutSliders, true);
|
||||||
SetDefault(OsuRulesetSetting.ShowCursorTrail, true);
|
SetDefault(OsuRulesetSetting.ShowCursorTrail, true);
|
||||||
|
SetDefault(OsuRulesetSetting.ShowCursorRipples, false);
|
||||||
SetDefault(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None);
|
SetDefault(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,6 +32,7 @@ namespace osu.Game.Rulesets.Osu.Configuration
|
|||||||
SnakingInSliders,
|
SnakingInSliders,
|
||||||
SnakingOutSliders,
|
SnakingOutSliders,
|
||||||
ShowCursorTrail,
|
ShowCursorTrail,
|
||||||
|
ShowCursorRipples,
|
||||||
PlayfieldBorderStyle,
|
PlayfieldBorderStyle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
|||||||
protected override bool AlwaysShowWhenSelected => true;
|
protected override bool AlwaysShowWhenSelected => true;
|
||||||
|
|
||||||
protected override bool ShouldBeAlive => base.ShouldBeAlive
|
protected override bool ShouldBeAlive => base.ShouldBeAlive
|
||||||
|| (ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION);
|
|| (DrawableObject is not DrawableSpinner && ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION);
|
||||||
|
|
||||||
protected OsuSelectionBlueprint(T hitObject)
|
protected OsuSelectionBlueprint(T hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
@ -83,11 +82,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
case SliderPlacementState.Initial:
|
case SliderPlacementState.Initial:
|
||||||
BeginPlacement();
|
BeginPlacement();
|
||||||
|
|
||||||
var nearestDifficultyPoint = editorBeatmap.HitObjects
|
double? nearestSliderVelocity = (editorBeatmap.HitObjects
|
||||||
.LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime)?
|
.LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocity;
|
||||||
.DifficultyControlPoint?.DeepClone() as DifficultyControlPoint;
|
|
||||||
|
|
||||||
HitObject.DifficultyControlPoint = nearestDifficultyPoint ?? new DifficultyControlPoint();
|
HitObject.SliderVelocity = nearestSliderVelocity ?? 1;
|
||||||
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
|
|
||||||
// Replacing the DifficultyControlPoint above doesn't trigger any kind of invalidation.
|
// Replacing the DifficultyControlPoint above doesn't trigger any kind of invalidation.
|
||||||
|
@ -14,7 +14,6 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -311,17 +310,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
var splitControlPoints = controlPoints.Take(index + 1).ToList();
|
var splitControlPoints = controlPoints.Take(index + 1).ToList();
|
||||||
controlPoints.RemoveRange(0, index);
|
controlPoints.RemoveRange(0, index);
|
||||||
|
|
||||||
// Turn the control points which were split off into a new slider.
|
|
||||||
var samplePoint = (SampleControlPoint)HitObject.SampleControlPoint.DeepClone();
|
|
||||||
var difficultyPoint = (DifficultyControlPoint)HitObject.DifficultyControlPoint.DeepClone();
|
|
||||||
|
|
||||||
var newSlider = new Slider
|
var newSlider = new Slider
|
||||||
{
|
{
|
||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
Position = HitObject.Position + splitControlPoints[0].Position,
|
Position = HitObject.Position + splitControlPoints[0].Position,
|
||||||
NewCombo = HitObject.NewCombo,
|
NewCombo = HitObject.NewCombo,
|
||||||
SampleControlPoint = samplePoint,
|
|
||||||
DifficultyControlPoint = difficultyPoint,
|
|
||||||
LegacyLastTickOffset = HitObject.LegacyLastTickOffset,
|
LegacyLastTickOffset = HitObject.LegacyLastTickOffset,
|
||||||
Samples = HitObject.Samples.Select(s => s.With()).ToList(),
|
Samples = HitObject.Samples.Select(s => s.With()).ToList(),
|
||||||
RepeatCount = HitObject.RepeatCount,
|
RepeatCount = HitObject.RepeatCount,
|
||||||
@ -378,15 +371,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
Vector2 position = HitObject.Position + HitObject.Path.PositionAt(pathPosition);
|
Vector2 position = HitObject.Position + HitObject.Path.PositionAt(pathPosition);
|
||||||
|
|
||||||
var samplePoint = (SampleControlPoint)HitObject.SampleControlPoint.DeepClone();
|
|
||||||
samplePoint.Time = time;
|
|
||||||
|
|
||||||
editorBeatmap.Add(new HitCircle
|
editorBeatmap.Add(new HitCircle
|
||||||
{
|
{
|
||||||
StartTime = time,
|
StartTime = time,
|
||||||
Position = position,
|
Position = position,
|
||||||
NewCombo = i == 0 && HitObject.NewCombo,
|
NewCombo = i == 0 && HitObject.NewCombo,
|
||||||
SampleControlPoint = samplePoint,
|
|
||||||
Samples = HitObject.HeadCircle.Samples.Select(s => s.With()).ToList()
|
Samples = HitObject.HeadCircle.Samples.Select(s => s.With()).ToList()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ using osu.Framework.Extensions.EnumExtensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
@ -62,7 +62,12 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
// Give a bit of breathing room around the playfield content.
|
// Give a bit of breathing room around the playfield content.
|
||||||
PlayfieldContentContainer.Padding = new MarginPadding(10);
|
PlayfieldContentContainer.Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Vertical = 10,
|
||||||
|
Left = TOOLBOX_CONTRACTED_SIZE_LEFT + 10,
|
||||||
|
Right = TOOLBOX_CONTRACTED_SIZE_RIGHT + 10,
|
||||||
|
};
|
||||||
|
|
||||||
LayerBelowRuleset.AddRange(new Drawable[]
|
LayerBelowRuleset.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -362,7 +362,6 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
StartTime = firstHitObject.StartTime,
|
StartTime = firstHitObject.StartTime,
|
||||||
Position = firstHitObject.Position,
|
Position = firstHitObject.Position,
|
||||||
NewCombo = firstHitObject.NewCombo,
|
NewCombo = firstHitObject.NewCombo,
|
||||||
SampleControlPoint = firstHitObject.SampleControlPoint,
|
|
||||||
Samples = firstHitObject.Samples,
|
Samples = firstHitObject.Samples,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -10,6 +11,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModBarrelRoll : ModBarrelRoll<OsuHitObject>, IApplicableToDrawableHitObject
|
public class OsuModBarrelRoll : ModBarrelRoll<OsuHitObject>, IApplicableToDrawableHitObject
|
||||||
{
|
{
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModBubbles) };
|
||||||
|
|
||||||
public void ApplyToDrawableHitObject(DrawableHitObject d)
|
public void ApplyToDrawableHitObject(DrawableHitObject d)
|
||||||
{
|
{
|
||||||
d.OnUpdate += _ =>
|
d.OnUpdate += _ =>
|
||||||
|
214
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
Normal file
214
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Graphics.Pooling;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
public partial class OsuModBubbles : Mod, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToDrawableHitObject, IApplicableToScoreProcessor
|
||||||
|
{
|
||||||
|
public override string Name => "Bubbles";
|
||||||
|
|
||||||
|
public override string Acronym => "BU";
|
||||||
|
|
||||||
|
public override LocalisableString Description => "Don't let their popping distract you!";
|
||||||
|
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
public override ModType Type => ModType.Fun;
|
||||||
|
|
||||||
|
// Compatibility with these seems potentially feasible in the future, blocked for now because they don't work as one would expect
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModBarrelRoll), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
||||||
|
|
||||||
|
private PlayfieldAdjustmentContainer bubbleContainer = null!;
|
||||||
|
|
||||||
|
private readonly Bindable<int> currentCombo = new BindableInt();
|
||||||
|
|
||||||
|
private float maxSize;
|
||||||
|
private float bubbleSize;
|
||||||
|
private double bubbleFade;
|
||||||
|
|
||||||
|
private readonly DrawablePool<BubbleDrawable> bubblePool = new DrawablePool<BubbleDrawable>(100);
|
||||||
|
|
||||||
|
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
||||||
|
|
||||||
|
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
|
||||||
|
{
|
||||||
|
currentCombo.BindTo(scoreProcessor.Combo);
|
||||||
|
currentCombo.BindValueChanged(combo =>
|
||||||
|
maxSize = Math.Min(1.75f, (float)(1.25 + 0.005 * combo.NewValue)), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
// Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen
|
||||||
|
// Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size
|
||||||
|
bubbleSize = (float)(drawableRuleset.Beatmap.HitObjects.OfType<HitCircle>().First().Radius * 1.90f);
|
||||||
|
bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType<HitCircle>().First().TimePreempt * 2;
|
||||||
|
|
||||||
|
// We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering)
|
||||||
|
drawableRuleset.Playfield.DisplayJudgements.Value = false;
|
||||||
|
|
||||||
|
bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer();
|
||||||
|
|
||||||
|
drawableRuleset.Overlays.Add(bubbleContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyToDrawableHitObject(DrawableHitObject drawableObject)
|
||||||
|
{
|
||||||
|
drawableObject.OnNewResult += (drawable, _) =>
|
||||||
|
{
|
||||||
|
if (drawable is not DrawableOsuHitObject drawableOsuHitObject) return;
|
||||||
|
|
||||||
|
switch (drawableOsuHitObject.HitObject)
|
||||||
|
{
|
||||||
|
case Slider:
|
||||||
|
case SpinnerTick:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
addBubble();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addBubble()
|
||||||
|
{
|
||||||
|
BubbleDrawable bubble = bubblePool.Get();
|
||||||
|
|
||||||
|
bubble.DrawableOsuHitObject = drawableOsuHitObject;
|
||||||
|
bubble.InitialSize = new Vector2(bubbleSize);
|
||||||
|
bubble.FadeTime = bubbleFade;
|
||||||
|
bubble.MaxSize = maxSize;
|
||||||
|
|
||||||
|
bubbleContainer.Add(bubble);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
drawableObject.OnRevertResult += (drawable, _) =>
|
||||||
|
{
|
||||||
|
if (drawable.HitObject is SpinnerTick or Slider) return;
|
||||||
|
|
||||||
|
BubbleDrawable? lastBubble = bubbleContainer.OfType<BubbleDrawable>().LastOrDefault();
|
||||||
|
|
||||||
|
lastBubble?.ClearTransforms();
|
||||||
|
lastBubble?.Expire(true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Pooled Bubble drawable
|
||||||
|
|
||||||
|
private partial class BubbleDrawable : PoolableDrawable
|
||||||
|
{
|
||||||
|
public DrawableOsuHitObject? DrawableOsuHitObject { get; set; }
|
||||||
|
|
||||||
|
public Vector2 InitialSize { get; set; }
|
||||||
|
|
||||||
|
public float MaxSize { get; set; }
|
||||||
|
|
||||||
|
public double FadeTime { get; set; }
|
||||||
|
|
||||||
|
private readonly Box colourBox;
|
||||||
|
private readonly CircularContainer content;
|
||||||
|
|
||||||
|
public BubbleDrawable()
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
InternalChild = content = new CircularContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
MaskingSmoothness = 2,
|
||||||
|
BorderThickness = 0,
|
||||||
|
BorderColour = Colour4.White,
|
||||||
|
Masking = true,
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Radius = 3,
|
||||||
|
Colour = Colour4.Black.Opacity(0.05f),
|
||||||
|
},
|
||||||
|
Child = colourBox = new Box { RelativeSizeAxes = Axes.Both, }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PrepareForUse()
|
||||||
|
{
|
||||||
|
Debug.Assert(DrawableOsuHitObject.IsNotNull());
|
||||||
|
|
||||||
|
Colour = DrawableOsuHitObject.IsHit ? Colour4.White : Colour4.Black;
|
||||||
|
Scale = new Vector2(1);
|
||||||
|
Position = getPosition(DrawableOsuHitObject);
|
||||||
|
Size = InitialSize;
|
||||||
|
|
||||||
|
//We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect.
|
||||||
|
ColourInfo colourDarker = DrawableOsuHitObject.AccentColour.Value.Darken(0.1f);
|
||||||
|
|
||||||
|
// The absolute length of the bubble's animation, can be used in fractions for animations of partial length
|
||||||
|
double duration = 1700 + Math.Pow(FadeTime, 1.07f);
|
||||||
|
|
||||||
|
// Main bubble scaling based on combo
|
||||||
|
this.FadeTo(1)
|
||||||
|
.ScaleTo(MaxSize, duration * 0.8f)
|
||||||
|
.Then()
|
||||||
|
// Pop at the end of the bubbles life time
|
||||||
|
.ScaleTo(MaxSize * 1.5f, duration * 0.2f, Easing.OutQuint)
|
||||||
|
.FadeOut(duration * 0.2f, Easing.OutCirc).Expire();
|
||||||
|
|
||||||
|
if (!DrawableOsuHitObject.IsHit) return;
|
||||||
|
|
||||||
|
content.BorderThickness = InitialSize.X / 3.5f;
|
||||||
|
content.BorderColour = Colour4.White;
|
||||||
|
|
||||||
|
colourBox.FadeColour(colourDarker);
|
||||||
|
|
||||||
|
content.TransformTo(nameof(BorderColour), colourDarker, duration * 0.3f, Easing.OutQuint);
|
||||||
|
// Ripple effect utilises the border to reduce drawable count
|
||||||
|
content.TransformTo(nameof(BorderThickness), 2f, duration * 0.3f, Easing.OutQuint)
|
||||||
|
.Then()
|
||||||
|
// Avoids transparency overlap issues during the bubble "pop"
|
||||||
|
.TransformTo(nameof(BorderThickness), 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 getPosition(DrawableOsuHitObject drawableObject)
|
||||||
|
{
|
||||||
|
switch (drawableObject)
|
||||||
|
{
|
||||||
|
// SliderHeads are derived from HitCircles,
|
||||||
|
// so we must handle them before to avoid them using the wrong positioning logic
|
||||||
|
case DrawableSliderHead:
|
||||||
|
return drawableObject.HitObject.Position;
|
||||||
|
|
||||||
|
// Using hitobject position will cause issues with HitCircle placement due to stack leniency.
|
||||||
|
case DrawableHitCircle:
|
||||||
|
return drawableObject.Position;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return drawableObject.HitObject.Position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModDaycore : ModDaycore
|
public class OsuModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModDoubleTime : ModDoubleTime
|
public class OsuModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModHalfTime : ModHalfTime
|
public class OsuModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
public override LocalisableString Description => "No need to chase the circles – your cursor is a magnet!";
|
public override LocalisableString Description => "No need to chase the circles – your cursor is a magnet!";
|
||||||
public override double ScoreMultiplier => 0.5;
|
public override double ScoreMultiplier => 0.5;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel), typeof(OsuModBubbles) };
|
||||||
|
|
||||||
[SettingSource("Attraction strength", "How strong the pull is.", 0)]
|
[SettingSource("Attraction strength", "How strong the pull is.", 0)]
|
||||||
public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f)
|
public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f)
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModNightcore : ModNightcore<OsuHitObject>
|
public class OsuModNightcore : ModNightcore<OsuHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
public override LocalisableString Description => "Hit objects run away!";
|
public override LocalisableString Description => "Hit objects run away!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModBubbles) };
|
||||||
|
|
||||||
[SettingSource("Repulsion strength", "How strong the repulsion is.", 0)]
|
[SettingSource("Repulsion strength", "How strong the repulsion is.", 0)]
|
||||||
public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f)
|
public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f)
|
||||||
|
@ -133,14 +133,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
// Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being.
|
// Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being.
|
||||||
|
|
||||||
if (HitObject.SampleControlPoint == null)
|
Samples.Samples = HitObject.TailSamples.Cast<ISampleInfo>().ToArray();
|
||||||
{
|
slidingSample.Samples = HitObject.CreateSlidingSamples().Cast<ISampleInfo>().ToArray();
|
||||||
throw new InvalidOperationException($"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
|
||||||
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Samples.Samples = HitObject.TailSamples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
|
||||||
slidingSample.Samples = HitObject.CreateSlidingSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void StopAllSamples()
|
public override void StopAllSamples()
|
||||||
|
@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.LoadSamples();
|
base.LoadSamples();
|
||||||
|
|
||||||
spinningSample.Samples = HitObject.CreateSpinningSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
spinningSample.Samples = HitObject.CreateSpinningSamples().Cast<ISampleInfo>().ToArray();
|
||||||
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,18 +10,18 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Formats;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
{
|
{
|
||||||
public class Slider : OsuHitObject, IHasPathWithRepeats
|
public class Slider : OsuHitObject, IHasPathWithRepeats, IHasSliderVelocity, IHasGenerateTicks
|
||||||
{
|
{
|
||||||
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
|
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
|
||||||
|
|
||||||
@ -134,6 +134,21 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool OnlyJudgeNestedObjects = true;
|
public bool OnlyJudgeNestedObjects = true;
|
||||||
|
|
||||||
|
public BindableNumber<double> SliderVelocityBindable { get; } = new BindableDouble(1)
|
||||||
|
{
|
||||||
|
Precision = 0.01,
|
||||||
|
MinValue = 0.1,
|
||||||
|
MaxValue = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
public double SliderVelocity
|
||||||
|
{
|
||||||
|
get => SliderVelocityBindable.Value;
|
||||||
|
set => SliderVelocityBindable.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GenerateTicks { get; set; } = true;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public SliderHeadCircle HeadCircle { get; protected set; }
|
public SliderHeadCircle HeadCircle { get; protected set; }
|
||||||
|
|
||||||
@ -151,15 +166,11 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||||
#pragma warning disable 618
|
|
||||||
var legacyDifficultyPoint = DifficultyControlPoint as LegacyBeatmapDecoder.LegacyDifficultyControlPoint;
|
|
||||||
#pragma warning restore 618
|
|
||||||
|
|
||||||
double scoringDistance = BASE_SCORING_DISTANCE * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
|
double scoringDistance = BASE_SCORING_DISTANCE * difficulty.SliderMultiplier * SliderVelocity;
|
||||||
bool generateTicks = legacyDifficultyPoint?.GenerateTicks ?? true;
|
|
||||||
|
|
||||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||||
TickDistance = generateTicks ? (scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier) : double.PositiveInfinity;
|
TickDistance = GenerateTicks ? (scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier) : double.PositiveInfinity;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
|
@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
AddNested(i < SpinsRequired
|
AddNested(i < SpinsRequired
|
||||||
? new SpinnerTick { StartTime = startTime, SpinnerDuration = Duration }
|
? new SpinnerTick { StartTime = startTime, SpinnerDuration = Duration }
|
||||||
: new SpinnerBonusTick { StartTime = startTime, SpinnerDuration = Duration });
|
: new SpinnerBonusTick { StartTime = startTime, SpinnerDuration = Duration, Samples = new[] { GetSampleInfo("spinnerbonus") } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
SampleControlPoint.ApplyTo(referenceSample).With("spinnerspin")
|
referenceSample.With("spinnerspin")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using osu.Game.Audio;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
@ -11,11 +10,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
public class SpinnerBonusTick : SpinnerTick
|
public class SpinnerBonusTick : SpinnerTick
|
||||||
{
|
{
|
||||||
public SpinnerBonusTick()
|
|
||||||
{
|
|
||||||
Samples.Add(new HitSampleInfo("spinnerbonus"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new OsuSpinnerBonusTickJudgement();
|
public override Judgement CreateJudgement() => new OsuSpinnerBonusTickJudgement();
|
||||||
|
|
||||||
public class OsuSpinnerBonusTickJudgement : OsuSpinnerTickJudgement
|
public class OsuSpinnerBonusTickJudgement : OsuSpinnerTickJudgement
|
||||||
|
@ -203,7 +203,8 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
new OsuModNoScope(),
|
new OsuModNoScope(),
|
||||||
new MultiMod(new OsuModMagnetised(), new OsuModRepel()),
|
new MultiMod(new OsuModMagnetised(), new OsuModRepel()),
|
||||||
new ModAdaptiveSpeed(),
|
new ModAdaptiveSpeed(),
|
||||||
new OsuModFreezeFrame()
|
new OsuModFreezeFrame(),
|
||||||
|
new OsuModBubbles()
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.System:
|
case ModType.System:
|
||||||
|
@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
Cursor,
|
Cursor,
|
||||||
CursorTrail,
|
CursorTrail,
|
||||||
CursorParticles,
|
CursorParticles,
|
||||||
|
CursorRipple,
|
||||||
SliderScorePoint,
|
SliderScorePoint,
|
||||||
ReverseArrow,
|
ReverseArrow,
|
||||||
HitCircleText,
|
HitCircleText,
|
||||||
|
@ -98,7 +98,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
|||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||||
{
|
{
|
||||||
this.FadeOut(duration, Easing.OutQuint);
|
// intentionally pile on an extra FadeOut to make it happen much faster
|
||||||
|
this.FadeOut(duration / 4, Easing.OutQuint);
|
||||||
icon.ScaleTo(defaultIconScale * icon_scale, duration, Easing.OutQuint);
|
icon.ScaleTo(defaultIconScale * icon_scale, duration, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,13 @@
|
|||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
{
|
{
|
||||||
@ -18,6 +20,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
private Drawable proxy = null!;
|
private Drawable proxy = null!;
|
||||||
|
|
||||||
|
private Bindable<Color4> accentColour = null!;
|
||||||
|
|
||||||
|
private bool textureIsDefaultSkin;
|
||||||
|
|
||||||
|
private Drawable arrow = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skinSource)
|
private void load(ISkinSource skinSource)
|
||||||
{
|
{
|
||||||
@ -26,7 +34,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
string lookupName = new OsuSkinComponentLookup(OsuSkinComponents.ReverseArrow).LookupName;
|
string lookupName = new OsuSkinComponentLookup(OsuSkinComponents.ReverseArrow).LookupName;
|
||||||
|
|
||||||
var skin = skinSource.FindProvider(s => s.GetTexture(lookupName) != null);
|
var skin = skinSource.FindProvider(s => s.GetTexture(lookupName) != null);
|
||||||
InternalChild = skin?.GetAnimation(lookupName, true, true) ?? Empty();
|
|
||||||
|
InternalChild = arrow = (skin?.GetAnimation(lookupName, true, true) ?? Empty());
|
||||||
|
textureIsDefaultSkin = skin is ISkinTransformer transformer && transformer.Skin is DefaultLegacySkin;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -39,6 +49,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
drawableHitObject.HitObjectApplied += onHitObjectApplied;
|
drawableHitObject.HitObjectApplied += onHitObjectApplied;
|
||||||
onHitObjectApplied(drawableHitObject);
|
onHitObjectApplied(drawableHitObject);
|
||||||
|
|
||||||
|
accentColour = drawableHitObject.AccentColour.GetBoundCopy();
|
||||||
|
accentColour.BindValueChanged(c =>
|
||||||
|
{
|
||||||
|
arrow.Colour = textureIsDefaultSkin && c.NewValue.R + c.NewValue.G + c.NewValue.B > (600 / 255f) ? Color4.Black : Color4.White;
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +100,28 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.CursorRipple:
|
||||||
|
if (GetTexture("cursor-ripple") != null)
|
||||||
|
{
|
||||||
|
var ripple = this.GetAnimation("cursor-ripple", false, false);
|
||||||
|
|
||||||
|
// In stable this element was scaled down to 50% and opacity 20%, but this makes the elements WAY too big and inflexible.
|
||||||
|
// If anyone complains about these not being applied, this can be uncommented.
|
||||||
|
//
|
||||||
|
// But if no one complains I'd rather fix this in lazer. Wiki documentation doesn't mention size,
|
||||||
|
// so we might be okay.
|
||||||
|
//
|
||||||
|
// if (ripple != null)
|
||||||
|
// {
|
||||||
|
// ripple.Scale = new Vector2(0.5f);
|
||||||
|
// ripple.Alpha = 0.2f;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return ripple;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
case OsuSkinComponents.CursorParticles:
|
case OsuSkinComponents.CursorParticles:
|
||||||
if (GetTexture("star2") != null)
|
if (GetTexture("star2") != null)
|
||||||
return new LegacyCursorParticles();
|
return new LegacyCursorParticles();
|
||||||
|
105
osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs
Normal file
105
osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Pooling;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||||
|
{
|
||||||
|
public partial class CursorRippleVisualiser : CompositeDrawable, IKeyBindingHandler<OsuAction>
|
||||||
|
{
|
||||||
|
private readonly Bindable<bool> showRipples = new Bindable<bool>(true);
|
||||||
|
|
||||||
|
private readonly DrawablePool<CursorRipple> ripplePool = new DrawablePool<CursorRipple>(20);
|
||||||
|
|
||||||
|
public CursorRippleVisualiser()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 CursorScale { get; set; } = Vector2.One;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(OsuRulesetConfigManager? rulesetConfig)
|
||||||
|
{
|
||||||
|
rulesetConfig?.BindWith(OsuRulesetSetting.ShowCursorRipples, showRipples);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||||
|
{
|
||||||
|
if (showRipples.Value)
|
||||||
|
{
|
||||||
|
AddInternal(ripplePool.Get(r =>
|
||||||
|
{
|
||||||
|
r.Position = e.MousePosition;
|
||||||
|
r.Scale = CursorScale;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class CursorRipple : PoolableDrawable
|
||||||
|
{
|
||||||
|
private Drawable ripple = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
InternalChild = ripple = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorRipple), _ => new DefaultCursorRipple())
|
||||||
|
{
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PrepareForUse()
|
||||||
|
{
|
||||||
|
base.PrepareForUse();
|
||||||
|
|
||||||
|
ClearTransforms(true);
|
||||||
|
|
||||||
|
ripple.ScaleTo(0.1f)
|
||||||
|
.ScaleTo(1, 700, Easing.Out);
|
||||||
|
|
||||||
|
this
|
||||||
|
.FadeOutFromOne(700)
|
||||||
|
.Expire(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class DefaultCursorRipple : CompositeDrawable
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new RingPiece(3)
|
||||||
|
{
|
||||||
|
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2),
|
||||||
|
Alpha = 0.1f,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
private Bindable<float> userCursorScale;
|
private Bindable<float> userCursorScale;
|
||||||
private Bindable<bool> autoCursorScale;
|
private Bindable<bool> autoCursorScale;
|
||||||
|
|
||||||
|
private readonly CursorRippleVisualiser rippleVisualiser;
|
||||||
|
|
||||||
public OsuCursorContainer()
|
public OsuCursorContainer()
|
||||||
{
|
{
|
||||||
InternalChild = fadeContainer = new Container
|
InternalChild = fadeContainer = new Container
|
||||||
@ -48,6 +50,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
cursorTrail = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling),
|
cursorTrail = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorTrail), _ => new DefaultCursorTrail(), confineMode: ConfineMode.NoScaling),
|
||||||
|
rippleVisualiser = new CursorRippleVisualiser(),
|
||||||
new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorParticles), confineMode: ConfineMode.NoScaling),
|
new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.CursorParticles), confineMode: ConfineMode.NoScaling),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -82,6 +85,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
var newScale = new Vector2(e.NewValue);
|
var newScale = new Vector2(e.NewValue);
|
||||||
|
|
||||||
ActiveCursor.Scale = newScale;
|
ActiveCursor.Scale = newScale;
|
||||||
|
rippleVisualiser.CursorScale = newScale;
|
||||||
cursorTrail.Scale = newScale;
|
cursorTrail.Scale = newScale;
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
@ -43,6 +43,11 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
LabelText = RulesetSettingsStrings.CursorTrail,
|
LabelText = RulesetSettingsStrings.CursorTrail,
|
||||||
Current = config.GetBindable<bool>(OsuRulesetSetting.ShowCursorTrail)
|
Current = config.GetBindable<bool>(OsuRulesetSetting.ShowCursorTrail)
|
||||||
},
|
},
|
||||||
|
new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = RulesetSettingsStrings.CursorRipples,
|
||||||
|
Current = config.GetBindable<bool>(OsuRulesetSetting.ShowCursorRipples)
|
||||||
|
},
|
||||||
new SettingsEnumDropdown<PlayfieldBorderStyle>
|
new SettingsEnumDropdown<PlayfieldBorderStyle>
|
||||||
{
|
{
|
||||||
LabelText = RulesetSettingsStrings.PlayfieldBorderStyle,
|
LabelText = RulesetSettingsStrings.PlayfieldBorderStyle,
|
||||||
|
@ -64,7 +64,9 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
|
|
||||||
foreach (HitObject hitObject in original.HitObjects)
|
foreach (HitObject hitObject in original.HitObjects)
|
||||||
{
|
{
|
||||||
double nextScrollSpeed = hitObject.DifficultyControlPoint.SliderVelocity;
|
if (hitObject is not IHasSliderVelocity hasSliderVelocity) continue;
|
||||||
|
|
||||||
|
double nextScrollSpeed = hasSliderVelocity.SliderVelocity;
|
||||||
EffectControlPoint currentEffectPoint = converted.ControlPointInfo.EffectPointAt(hitObject.StartTime);
|
EffectControlPoint currentEffectPoint = converted.ControlPointInfo.EffectPointAt(hitObject.StartTime);
|
||||||
|
|
||||||
if (!Precision.AlmostEquals(lastScrollSpeed, nextScrollSpeed, acceptableDifference: currentEffectPoint.ScrollSpeedBindable.Precision))
|
if (!Precision.AlmostEquals(lastScrollSpeed, nextScrollSpeed, acceptableDifference: currentEffectPoint.ScrollSpeedBindable.Precision))
|
||||||
@ -131,7 +133,8 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Samples = obj.Samples,
|
Samples = obj.Samples,
|
||||||
Duration = taikoDuration,
|
Duration = taikoDuration,
|
||||||
TickRate = beatmap.Difficulty.SliderTickRate == 3 ? 3 : 4
|
TickRate = beatmap.Difficulty.SliderTickRate == 3 ? 3 : 4,
|
||||||
|
SliderVelocity = obj is IHasSliderVelocity velocityData ? velocityData.SliderVelocity : 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,15 +180,14 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
double distance = distanceData.Distance * spans * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER;
|
double distance = distanceData.Distance * spans * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER;
|
||||||
|
|
||||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
|
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
|
||||||
DifficultyControlPoint difficultyPoint = obj.DifficultyControlPoint;
|
|
||||||
|
|
||||||
double beatLength;
|
double beatLength;
|
||||||
#pragma warning disable 618
|
if (obj.LegacyBpmMultiplier.HasValue)
|
||||||
if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint)
|
beatLength = timingPoint.BeatLength * obj.LegacyBpmMultiplier.Value;
|
||||||
#pragma warning restore 618
|
else if (obj is IHasSliderVelocity hasSliderVelocity)
|
||||||
beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier;
|
beatLength = timingPoint.BeatLength / hasSliderVelocity.SliderVelocity;
|
||||||
else
|
else
|
||||||
beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity;
|
beatLength = timingPoint.BeatLength;
|
||||||
|
|
||||||
double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.Difficulty.SliderMultiplier / beatmap.Difficulty.SliderTickRate;
|
double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.Difficulty.SliderMultiplier / beatmap.Difficulty.SliderTickRate;
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -35,20 +33,11 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
switch (e.Button)
|
if (e.Button != MouseButton.Left)
|
||||||
{
|
return false;
|
||||||
case MouseButton.Left:
|
|
||||||
HitObject.Type = HitType.Centre;
|
|
||||||
EndPlacement(true);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case MouseButton.Right:
|
EndPlacement(true);
|
||||||
HitObject.Type = HitType.Rim;
|
return true;
|
||||||
EndPlacement(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset;
|
var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset;
|
||||||
drawableTaikoRuleset.LockPlayfieldMaxAspect.Value = false;
|
drawableTaikoRuleset.LockPlayfieldAspectRange.Value = false;
|
||||||
|
|
||||||
var playfield = (TaikoPlayfield)drawableRuleset.Playfield;
|
var playfield = (TaikoPlayfield)drawableRuleset.Playfield;
|
||||||
playfield.ClassicHitTargetPosition.Value = true;
|
playfield.ClassicHitTargetPosition.Value = true;
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModDaycore : ModDaycore
|
public class TaikoModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModDoubleTime : ModDoubleTime
|
public class TaikoModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModHalfTime : ModHalfTime
|
public class TaikoModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModNightcore : ModNightcore<TaikoHitObject>
|
public class TaikoModNightcore : ModNightcore<TaikoHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,11 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
@ -15,7 +18,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects
|
namespace osu.Game.Rulesets.Taiko.Objects
|
||||||
{
|
{
|
||||||
public class DrumRoll : TaikoStrongableHitObject, IHasPath
|
public class DrumRoll : TaikoStrongableHitObject, IHasPath, IHasSliderVelocity
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
|
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
|
||||||
@ -35,6 +38,19 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public double Velocity { get; private set; }
|
public double Velocity { get; private set; }
|
||||||
|
|
||||||
|
public BindableNumber<double> SliderVelocityBindable { get; } = new BindableDouble(1)
|
||||||
|
{
|
||||||
|
Precision = 0.01,
|
||||||
|
MinValue = 0.1,
|
||||||
|
MaxValue = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
public double SliderVelocity
|
||||||
|
{
|
||||||
|
get => SliderVelocityBindable.Value;
|
||||||
|
set => SliderVelocityBindable.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Numer of ticks per beat length.
|
/// Numer of ticks per beat length.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -52,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
|
|
||||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||||
|
|
||||||
double scoringDistance = base_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
|
double scoringDistance = base_distance * difficulty.SliderMultiplier * SliderVelocity;
|
||||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||||
|
|
||||||
tickSpacing = timingPoint.BeatLength / TickRate;
|
tickSpacing = timingPoint.BeatLength / TickRate;
|
||||||
@ -81,7 +97,8 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
FirstTick = first,
|
FirstTick = first,
|
||||||
TickSpacing = tickSpacing,
|
TickSpacing = tickSpacing,
|
||||||
StartTime = t,
|
StartTime = t,
|
||||||
IsStrong = IsStrong
|
IsStrong = IsStrong,
|
||||||
|
Samples = Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToList()
|
||||||
});
|
});
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
if (IsStrongBindable.Value != strongSamples.Any())
|
if (IsStrongBindable.Value != strongSamples.Any())
|
||||||
{
|
{
|
||||||
if (IsStrongBindable.Value)
|
if (IsStrongBindable.Value)
|
||||||
Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH));
|
Samples.Add(GetSampleInfo(HitSampleInfo.HIT_FINISH));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var sample in strongSamples)
|
foreach (var sample in strongSamples)
|
||||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
public new BindableDouble TimeRange => base.TimeRange;
|
public new BindableDouble TimeRange => base.TimeRange;
|
||||||
|
|
||||||
public readonly BindableBool LockPlayfieldMaxAspect = new BindableBool(true);
|
public readonly BindableBool LockPlayfieldAspectRange = new BindableBool(true);
|
||||||
|
|
||||||
public new TaikoInputManager KeyBindingInputManager => (TaikoInputManager)base.KeyBindingInputManager;
|
public new TaikoInputManager KeyBindingInputManager => (TaikoInputManager)base.KeyBindingInputManager;
|
||||||
|
|
||||||
@ -69,7 +69,9 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
const float scroll_rate = 10;
|
const float scroll_rate = 10;
|
||||||
|
|
||||||
// Since the time range will depend on a positional value, it is referenced to the x480 pixel space.
|
// Since the time range will depend on a positional value, it is referenced to the x480 pixel space.
|
||||||
float ratio = DrawHeight / 480;
|
// Width is used because it defines how many notes fit on the playfield.
|
||||||
|
// We clamp the ratio to the maximum aspect ratio to keep scroll speed consistent on widths lower than the default.
|
||||||
|
float ratio = Math.Max(DrawSize.X / 768f, TaikoPlayfieldAdjustmentContainer.MAXIMUM_ASPECT);
|
||||||
|
|
||||||
TimeRange.Value = (Playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate;
|
TimeRange.Value = (Playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate;
|
||||||
}
|
}
|
||||||
@ -92,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer
|
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer
|
||||||
{
|
{
|
||||||
LockPlayfieldMaxAspect = { BindTarget = LockPlayfieldMaxAspect }
|
LockPlayfieldAspectRange = { BindTarget = LockPlayfieldAspectRange }
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
@ -17,12 +18,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
public void Play(HitType hitType)
|
public void Play(HitType hitType)
|
||||||
{
|
{
|
||||||
var hitObject = GetMostValidObject();
|
var hitSample = GetMostValidObject()?.Samples?.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL);
|
||||||
|
|
||||||
if (hitObject == null)
|
if (hitSample == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PlaySamples(new ISampleInfo[] { hitObject.SampleControlPoint.GetSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL) });
|
PlaySamples(new ISampleInfo[] { new HitSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL, hitSample.Bank, volume: hitSample.Volume) });
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Play() => throw new InvalidOperationException(@"Use override with HitType parameter instead");
|
public override void Play() => throw new InvalidOperationException(@"Use override with HitType parameter instead");
|
||||||
|
@ -11,9 +11,11 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
public partial class TaikoPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer
|
public partial class TaikoPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer
|
||||||
{
|
{
|
||||||
private const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768;
|
private const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768;
|
||||||
private const float default_aspect = 16f / 9f;
|
|
||||||
|
|
||||||
public readonly IBindable<bool> LockPlayfieldMaxAspect = new BindableBool(true);
|
public const float MAXIMUM_ASPECT = 16f / 9f;
|
||||||
|
public const float MINIMUM_ASPECT = 5f / 4f;
|
||||||
|
|
||||||
|
public readonly IBindable<bool> LockPlayfieldAspectRange = new BindableBool(true);
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
@ -26,12 +28,22 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
//
|
//
|
||||||
// As a middle-ground, the aspect ratio can still be adjusted in the downwards direction but has a maximum limit.
|
// As a middle-ground, the aspect ratio can still be adjusted in the downwards direction but has a maximum limit.
|
||||||
// This is still a bit weird, because readability changes with window size, but it is what it is.
|
// This is still a bit weird, because readability changes with window size, but it is what it is.
|
||||||
if (LockPlayfieldMaxAspect.Value && Parent.ChildSize.X / Parent.ChildSize.Y > default_aspect)
|
if (LockPlayfieldAspectRange.Value)
|
||||||
height *= Math.Clamp(Parent.ChildSize.X / Parent.ChildSize.Y, 0.4f, 4) / default_aspect;
|
{
|
||||||
|
float currentAspect = Parent.ChildSize.X / Parent.ChildSize.Y;
|
||||||
|
|
||||||
|
if (currentAspect > MAXIMUM_ASPECT)
|
||||||
|
height *= currentAspect / MAXIMUM_ASPECT;
|
||||||
|
else if (currentAspect < MINIMUM_ASPECT)
|
||||||
|
height *= currentAspect / MINIMUM_ASPECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit the maximum relative height of the playfield to one-third of available area to avoid it masking out on extreme resolutions.
|
||||||
|
height = Math.Min(height, 1f / 3f);
|
||||||
Height = height;
|
Height = height;
|
||||||
|
|
||||||
// Position the taiko playfield exactly one playfield from the top of the screen.
|
// Position the taiko playfield exactly one playfield from the top of the screen, if there is enough space for it.
|
||||||
|
// Note that the relative height cannot exceed one-third - if that limit is hit, the playfield will be exactly centered.
|
||||||
RelativePositionAxes = Axes.Y;
|
RelativePositionAxes = Axes.Y;
|
||||||
Y = height;
|
Y = height;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -161,6 +160,51 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeVideoWithLowercaseExtension()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("video-with-lowercase-extension.osb"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var beatmap = decoder.Decode(stream);
|
||||||
|
var metadata = beatmap.Metadata;
|
||||||
|
|
||||||
|
Assert.AreEqual("BG.jpg", metadata.BackgroundFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeVideoWithUppercaseExtension()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("video-with-uppercase-extension.osb"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var beatmap = decoder.Decode(stream);
|
||||||
|
var metadata = beatmap.Metadata;
|
||||||
|
|
||||||
|
Assert.AreEqual("BG.jpg", metadata.BackgroundFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeImageSpecifiedAsVideo()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("image-specified-as-video.osb"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var beatmap = decoder.Decode(stream);
|
||||||
|
var metadata = beatmap.Metadata;
|
||||||
|
|
||||||
|
Assert.AreEqual("BG.jpg", metadata.BackgroundFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeBeatmapTimingPoints()
|
public void TestDecodeBeatmapTimingPoints()
|
||||||
{
|
{
|
||||||
@ -320,6 +364,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var comboColors = decoder.Decode(stream).ComboColours;
|
var comboColors = decoder.Decode(stream).ComboColours;
|
||||||
|
|
||||||
|
Debug.Assert(comboColors != null);
|
||||||
|
|
||||||
Color4[] expectedColors =
|
Color4[] expectedColors =
|
||||||
{
|
{
|
||||||
new Color4(142, 199, 255, 255),
|
new Color4(142, 199, 255, 255),
|
||||||
@ -330,7 +376,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
new Color4(255, 177, 140, 255),
|
new Color4(255, 177, 140, 255),
|
||||||
new Color4(100, 100, 100, 255), // alpha is specified as 100, but should be ignored.
|
new Color4(100, 100, 100, 255), // alpha is specified as 100, but should be ignored.
|
||||||
};
|
};
|
||||||
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++)
|
||||||
Assert.AreEqual(expectedColors[i], comboColors[i]);
|
Assert.AreEqual(expectedColors[i], comboColors[i]);
|
||||||
}
|
}
|
||||||
@ -415,14 +461,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
|
|
||||||
Assert.IsNotNull(positionData);
|
Assert.IsNotNull(positionData);
|
||||||
Assert.IsNotNull(curveData);
|
Assert.IsNotNull(curveData);
|
||||||
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
|
Assert.AreEqual(new Vector2(192, 168), positionData!.Position);
|
||||||
Assert.AreEqual(956, hitObjects[0].StartTime);
|
Assert.AreEqual(956, hitObjects[0].StartTime);
|
||||||
Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL));
|
Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL));
|
||||||
|
|
||||||
positionData = hitObjects[1] as IHasPosition;
|
positionData = hitObjects[1] as IHasPosition;
|
||||||
|
|
||||||
Assert.IsNotNull(positionData);
|
Assert.IsNotNull(positionData);
|
||||||
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
|
Assert.AreEqual(new Vector2(304, 56), positionData!.Position);
|
||||||
Assert.AreEqual(1285, hitObjects[1].StartTime);
|
Assert.AreEqual(1285, hitObjects[1].StartTime);
|
||||||
Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP));
|
Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP));
|
||||||
}
|
}
|
||||||
@ -464,7 +510,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual("Gameplay/soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
|
Assert.AreEqual("Gameplay/soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.Samples[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -482,7 +528,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual("Gameplay/normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
Assert.AreEqual("Gameplay/normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.Samples[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -502,7 +548,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
|
Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.Samples[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -578,8 +624,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestFallbackDecoderForCorruptedHeader()
|
public void TestFallbackDecoderForCorruptedHeader()
|
||||||
{
|
{
|
||||||
Decoder<Beatmap> decoder = null;
|
Decoder<Beatmap> decoder = null!;
|
||||||
Beatmap beatmap = null;
|
Beatmap beatmap = null!;
|
||||||
|
|
||||||
using (var resStream = TestResources.OpenResource("corrupted-header.osu"))
|
using (var resStream = TestResources.OpenResource("corrupted-header.osu"))
|
||||||
using (var stream = new LineBufferedReader(resStream))
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
@ -596,8 +642,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestFallbackDecoderForMissingHeader()
|
public void TestFallbackDecoderForMissingHeader()
|
||||||
{
|
{
|
||||||
Decoder<Beatmap> decoder = null;
|
Decoder<Beatmap> decoder = null!;
|
||||||
Beatmap beatmap = null;
|
Beatmap beatmap = null!;
|
||||||
|
|
||||||
using (var resStream = TestResources.OpenResource("missing-header.osu"))
|
using (var resStream = TestResources.OpenResource("missing-header.osu"))
|
||||||
using (var stream = new LineBufferedReader(resStream))
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
@ -614,8 +660,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeFileWithEmptyLinesAtStart()
|
public void TestDecodeFileWithEmptyLinesAtStart()
|
||||||
{
|
{
|
||||||
Decoder<Beatmap> decoder = null;
|
Decoder<Beatmap> decoder = null!;
|
||||||
Beatmap beatmap = null;
|
Beatmap beatmap = null!;
|
||||||
|
|
||||||
using (var resStream = TestResources.OpenResource("empty-lines-at-start.osu"))
|
using (var resStream = TestResources.OpenResource("empty-lines-at-start.osu"))
|
||||||
using (var stream = new LineBufferedReader(resStream))
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
@ -632,8 +678,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeFileWithEmptyLinesAndNoHeader()
|
public void TestDecodeFileWithEmptyLinesAndNoHeader()
|
||||||
{
|
{
|
||||||
Decoder<Beatmap> decoder = null;
|
Decoder<Beatmap> decoder = null!;
|
||||||
Beatmap beatmap = null;
|
Beatmap beatmap = null!;
|
||||||
|
|
||||||
using (var resStream = TestResources.OpenResource("empty-line-instead-of-header.osu"))
|
using (var resStream = TestResources.OpenResource("empty-line-instead-of-header.osu"))
|
||||||
using (var stream = new LineBufferedReader(resStream))
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
@ -650,8 +696,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeFileWithContentImmediatelyAfterHeader()
|
public void TestDecodeFileWithContentImmediatelyAfterHeader()
|
||||||
{
|
{
|
||||||
Decoder<Beatmap> decoder = null;
|
Decoder<Beatmap> decoder = null!;
|
||||||
Beatmap beatmap = null;
|
Beatmap beatmap = null!;
|
||||||
|
|
||||||
using (var resStream = TestResources.OpenResource("no-empty-line-after-header.osu"))
|
using (var resStream = TestResources.OpenResource("no-empty-line-after-header.osu"))
|
||||||
using (var stream = new LineBufferedReader(resStream))
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
@ -678,7 +724,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestAllowFallbackDecoderOverwrite()
|
public void TestAllowFallbackDecoderOverwrite()
|
||||||
{
|
{
|
||||||
Decoder<Beatmap> decoder = null;
|
Decoder<Beatmap> decoder = null!;
|
||||||
|
|
||||||
using (var resStream = TestResources.OpenResource("corrupted-header.osu"))
|
using (var resStream = TestResources.OpenResource("corrupted-header.osu"))
|
||||||
using (var stream = new LineBufferedReader(resStream))
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -30,35 +28,35 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.IsTrue(storyboard.HasDrawable);
|
Assert.IsTrue(storyboard.HasDrawable);
|
||||||
Assert.AreEqual(6, storyboard.Layers.Count());
|
Assert.AreEqual(6, storyboard.Layers.Count());
|
||||||
|
|
||||||
StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
|
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
|
||||||
Assert.IsNotNull(background);
|
Assert.IsNotNull(background);
|
||||||
Assert.AreEqual(16, background.Elements.Count);
|
Assert.AreEqual(16, background.Elements.Count);
|
||||||
Assert.IsTrue(background.VisibleWhenFailing);
|
Assert.IsTrue(background.VisibleWhenFailing);
|
||||||
Assert.IsTrue(background.VisibleWhenPassing);
|
Assert.IsTrue(background.VisibleWhenPassing);
|
||||||
Assert.AreEqual("Background", background.Name);
|
Assert.AreEqual("Background", background.Name);
|
||||||
|
|
||||||
StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2);
|
StoryboardLayer fail = storyboard.Layers.Single(l => l.Depth == 2);
|
||||||
Assert.IsNotNull(fail);
|
Assert.IsNotNull(fail);
|
||||||
Assert.AreEqual(0, fail.Elements.Count);
|
Assert.AreEqual(0, fail.Elements.Count);
|
||||||
Assert.IsTrue(fail.VisibleWhenFailing);
|
Assert.IsTrue(fail.VisibleWhenFailing);
|
||||||
Assert.IsFalse(fail.VisibleWhenPassing);
|
Assert.IsFalse(fail.VisibleWhenPassing);
|
||||||
Assert.AreEqual("Fail", fail.Name);
|
Assert.AreEqual("Fail", fail.Name);
|
||||||
|
|
||||||
StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1);
|
StoryboardLayer pass = storyboard.Layers.Single(l => l.Depth == 1);
|
||||||
Assert.IsNotNull(pass);
|
Assert.IsNotNull(pass);
|
||||||
Assert.AreEqual(0, pass.Elements.Count);
|
Assert.AreEqual(0, pass.Elements.Count);
|
||||||
Assert.IsFalse(pass.VisibleWhenFailing);
|
Assert.IsFalse(pass.VisibleWhenFailing);
|
||||||
Assert.IsTrue(pass.VisibleWhenPassing);
|
Assert.IsTrue(pass.VisibleWhenPassing);
|
||||||
Assert.AreEqual("Pass", pass.Name);
|
Assert.AreEqual("Pass", pass.Name);
|
||||||
|
|
||||||
StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0);
|
StoryboardLayer foreground = storyboard.Layers.Single(l => l.Depth == 0);
|
||||||
Assert.IsNotNull(foreground);
|
Assert.IsNotNull(foreground);
|
||||||
Assert.AreEqual(151, foreground.Elements.Count);
|
Assert.AreEqual(151, foreground.Elements.Count);
|
||||||
Assert.IsTrue(foreground.VisibleWhenFailing);
|
Assert.IsTrue(foreground.VisibleWhenFailing);
|
||||||
Assert.IsTrue(foreground.VisibleWhenPassing);
|
Assert.IsTrue(foreground.VisibleWhenPassing);
|
||||||
Assert.AreEqual("Foreground", foreground.Name);
|
Assert.AreEqual("Foreground", foreground.Name);
|
||||||
|
|
||||||
StoryboardLayer overlay = storyboard.Layers.FirstOrDefault(l => l.Depth == int.MinValue);
|
StoryboardLayer overlay = storyboard.Layers.Single(l => l.Depth == int.MinValue);
|
||||||
Assert.IsNotNull(overlay);
|
Assert.IsNotNull(overlay);
|
||||||
Assert.IsEmpty(overlay.Elements);
|
Assert.IsEmpty(overlay.Elements);
|
||||||
Assert.IsTrue(overlay.VisibleWhenFailing);
|
Assert.IsTrue(overlay.VisibleWhenFailing);
|
||||||
@ -76,7 +74,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
|
|
||||||
var sprite = background.Elements.ElementAt(0) as StoryboardSprite;
|
var sprite = background.Elements.ElementAt(0) as StoryboardSprite;
|
||||||
Assert.NotNull(sprite);
|
Assert.NotNull(sprite);
|
||||||
Assert.IsTrue(sprite.HasCommands);
|
Assert.IsTrue(sprite!.HasCommands);
|
||||||
Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
|
Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
|
||||||
Assert.IsTrue(sprite.IsDrawable);
|
Assert.IsTrue(sprite.IsDrawable);
|
||||||
Assert.AreEqual(Anchor.Centre, sprite.Origin);
|
Assert.AreEqual(Anchor.Centre, sprite.Origin);
|
||||||
@ -97,6 +95,27 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLoopWithoutExplicitFadeOut()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyStoryboardDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("animation-loop-no-explicit-end-time.osb"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var storyboard = decoder.Decode(stream);
|
||||||
|
|
||||||
|
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
|
||||||
|
Assert.AreEqual(1, background.Elements.Count);
|
||||||
|
|
||||||
|
Assert.AreEqual(2000, background.Elements[0].StartTime);
|
||||||
|
Assert.AreEqual(2000, (background.Elements[0] as StoryboardAnimation)?.EarliestTransformTime);
|
||||||
|
|
||||||
|
Assert.AreEqual(3000, (background.Elements[0] as StoryboardAnimation)?.GetEndTime());
|
||||||
|
Assert.AreEqual(12000, (background.Elements[0] as StoryboardAnimation)?.EndTimeForDisplay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCorrectAnimationStartTime()
|
public void TestCorrectAnimationStartTime()
|
||||||
{
|
{
|
||||||
@ -171,6 +190,55 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeVideoWithLowercaseExtension()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyStoryboardDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("video-with-lowercase-extension.osb"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var storyboard = decoder.Decode(stream);
|
||||||
|
|
||||||
|
StoryboardLayer video = storyboard.Layers.Single(l => l.Name == "Video");
|
||||||
|
Assert.That(video.Elements.Count, Is.EqualTo(1));
|
||||||
|
|
||||||
|
Assert.AreEqual("Video.avi", ((StoryboardVideo)video.Elements[0]).Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeVideoWithUppercaseExtension()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyStoryboardDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("video-with-uppercase-extension.osb"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var storyboard = decoder.Decode(stream);
|
||||||
|
|
||||||
|
StoryboardLayer video = storyboard.Layers.Single(l => l.Name == "Video");
|
||||||
|
Assert.That(video.Elements.Count, Is.EqualTo(1));
|
||||||
|
|
||||||
|
Assert.AreEqual("Video.AVI", ((StoryboardVideo)video.Elements[0]).Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeImageSpecifiedAsVideo()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyStoryboardDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("image-specified-as-video.osb"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var storyboard = decoder.Decode(stream);
|
||||||
|
|
||||||
|
StoryboardLayer video = storyboard.Layers.Single(l => l.Name == "Video");
|
||||||
|
Assert.That(video.Elements.Count, Is.Zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeOutOfRangeLoopAnimationType()
|
public void TestDecodeOutOfRangeLoopAnimationType()
|
||||||
{
|
{
|
||||||
|
@ -37,45 +37,6 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
cpi.Add(2000, new SampleControlPoint { SampleVolume = volume_muted });
|
cpi.Add(2000, new SampleControlPoint { SampleVolume = volume_muted });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestNormalControlPointVolume()
|
|
||||||
{
|
|
||||||
var hitCircle = new HitCircle
|
|
||||||
{
|
|
||||||
StartTime = 0,
|
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
|
||||||
};
|
|
||||||
hitCircle.ApplyDefaults(cpi, new BeatmapDifficulty());
|
|
||||||
|
|
||||||
assertOk(new List<HitObject> { hitCircle });
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestLowControlPointVolume()
|
|
||||||
{
|
|
||||||
var hitCircle = new HitCircle
|
|
||||||
{
|
|
||||||
StartTime = 1000,
|
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
|
||||||
};
|
|
||||||
hitCircle.ApplyDefaults(cpi, new BeatmapDifficulty());
|
|
||||||
|
|
||||||
assertLowVolume(new List<HitObject> { hitCircle });
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestMutedControlPointVolume()
|
|
||||||
{
|
|
||||||
var hitCircle = new HitCircle
|
|
||||||
{
|
|
||||||
StartTime = 2000,
|
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
|
||||||
};
|
|
||||||
hitCircle.ApplyDefaults(cpi, new BeatmapDifficulty());
|
|
||||||
|
|
||||||
assertMuted(new List<HitObject> { hitCircle });
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestNormalSampleVolume()
|
public void TestNormalSampleVolume()
|
||||||
{
|
{
|
||||||
@ -122,7 +83,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
var sliderHead = new SliderHeadCircle
|
var sliderHead = new SliderHeadCircle
|
||||||
{
|
{
|
||||||
StartTime = 0,
|
StartTime = 0,
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_regular) }
|
||||||
};
|
};
|
||||||
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
@ -135,7 +96,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
|
|
||||||
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 0, endTime: 500)
|
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 0, endTime: 500)
|
||||||
{
|
{
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_regular) }
|
||||||
};
|
};
|
||||||
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
@ -155,13 +116,13 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
var sliderTick = new SliderTick
|
var sliderTick = new SliderTick
|
||||||
{
|
{
|
||||||
StartTime = 250,
|
StartTime = 250,
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick") }
|
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick", volume: volume_regular) }
|
||||||
};
|
};
|
||||||
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 0, endTime: 500)
|
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 0, endTime: 500)
|
||||||
{
|
{
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) } // Applies to the tail.
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_regular) } // Applies to the tail.
|
||||||
};
|
};
|
||||||
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
@ -174,14 +135,14 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
var sliderHead = new SliderHeadCircle
|
var sliderHead = new SliderHeadCircle
|
||||||
{
|
{
|
||||||
StartTime = 0,
|
StartTime = 0,
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_regular) }
|
||||||
};
|
};
|
||||||
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
var sliderTick = new SliderTick
|
var sliderTick = new SliderTick
|
||||||
{
|
{
|
||||||
StartTime = 250,
|
StartTime = 250,
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick") }
|
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick", volume: volume_regular) }
|
||||||
};
|
};
|
||||||
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
@ -194,59 +155,6 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
assertMutedPassive(new List<HitObject> { slider });
|
assertMutedPassive(new List<HitObject> { slider });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestMutedControlPointVolumeSliderHead()
|
|
||||||
{
|
|
||||||
var sliderHead = new SliderHeadCircle
|
|
||||||
{
|
|
||||||
StartTime = 2000,
|
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
|
||||||
};
|
|
||||||
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
|
||||||
|
|
||||||
var sliderTick = new SliderTick
|
|
||||||
{
|
|
||||||
StartTime = 2250,
|
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick") }
|
|
||||||
};
|
|
||||||
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
|
||||||
|
|
||||||
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 2000, endTime: 2500)
|
|
||||||
{
|
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_regular) }
|
|
||||||
};
|
|
||||||
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
|
||||||
|
|
||||||
assertMuted(new List<HitObject> { slider });
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestMutedControlPointVolumeSliderTail()
|
|
||||||
{
|
|
||||||
var sliderHead = new SliderHeadCircle
|
|
||||||
{
|
|
||||||
StartTime = 0,
|
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
|
||||||
};
|
|
||||||
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
|
||||||
|
|
||||||
var sliderTick = new SliderTick
|
|
||||||
{
|
|
||||||
StartTime = 250,
|
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick") }
|
|
||||||
};
|
|
||||||
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
|
||||||
|
|
||||||
// Ends after the 5% control point.
|
|
||||||
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 0, endTime: 2500)
|
|
||||||
{
|
|
||||||
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
|
||||||
};
|
|
||||||
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
|
||||||
|
|
||||||
assertMutedPassive(new List<HitObject> { slider });
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertOk(List<HitObject> hitObjects)
|
private void assertOk(List<HitObject> hitObjects)
|
||||||
{
|
{
|
||||||
Assert.That(check.Run(getContext(hitObjects)), Is.Empty);
|
Assert.That(check.Run(getContext(hitObjects)), Is.Empty);
|
||||||
|
@ -14,6 +14,7 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
using osu.Game.Rulesets.Osu.Edit;
|
using osu.Game.Rulesets.Osu.Edit;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
@ -74,12 +75,9 @@ namespace osu.Game.Tests.Editing
|
|||||||
[TestCase(2)]
|
[TestCase(2)]
|
||||||
public void TestSpeedMultiplierDoesNotChangeDistanceSnap(float multiplier)
|
public void TestSpeedMultiplierDoesNotChangeDistanceSnap(float multiplier)
|
||||||
{
|
{
|
||||||
assertSnapDistance(100, new HitObject
|
assertSnapDistance(100, new Slider
|
||||||
{
|
{
|
||||||
DifficultyControlPoint = new DifficultyControlPoint
|
SliderVelocity = multiplier
|
||||||
{
|
|
||||||
SliderVelocity = multiplier
|
|
||||||
}
|
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,12 +85,9 @@ namespace osu.Game.Tests.Editing
|
|||||||
[TestCase(2)]
|
[TestCase(2)]
|
||||||
public void TestSpeedMultiplierDoesChangeDistanceSnap(float multiplier)
|
public void TestSpeedMultiplierDoesChangeDistanceSnap(float multiplier)
|
||||||
{
|
{
|
||||||
assertSnapDistance(100 * multiplier, new HitObject
|
assertSnapDistance(100 * multiplier, new Slider
|
||||||
{
|
{
|
||||||
DifficultyControlPoint = new DifficultyControlPoint
|
SliderVelocity = multiplier
|
||||||
{
|
|
||||||
SliderVelocity = multiplier
|
|
||||||
}
|
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,12 +109,9 @@ namespace osu.Game.Tests.Editing
|
|||||||
const float base_distance = 100;
|
const float base_distance = 100;
|
||||||
const float slider_velocity = 1.2f;
|
const float slider_velocity = 1.2f;
|
||||||
|
|
||||||
var referenceObject = new HitObject
|
var referenceObject = new Slider
|
||||||
{
|
{
|
||||||
DifficultyControlPoint = new DifficultyControlPoint
|
SliderVelocity = slider_velocity
|
||||||
{
|
|
||||||
SliderVelocity = slider_velocity
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assertSnapDistance(base_distance * slider_velocity, referenceObject, true);
|
assertSnapDistance(base_distance * slider_velocity, referenceObject, true);
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
#include "sh_Utils.h"
|
#define HIGH_PRECISION_VERTEX
|
||||||
|
|
||||||
varying mediump vec2 v_TexCoord;
|
#include "sh_Utils.h"
|
||||||
varying mediump vec4 v_TexRect;
|
#include "sh_Masking.h"
|
||||||
|
|
||||||
|
layout(location = 2) in highp vec2 v_TexCoord;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Colour;
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
float hueValue = v_TexCoord.x / (v_TexRect[2] - v_TexRect[0]);
|
highp float hueValue = v_TexCoord.x / (v_TexRect[2] - v_TexRect[0]);
|
||||||
gl_FragColor = hsv2rgb(vec4(hueValue, 1, 1, 1));
|
o_Colour = getRoundedColor(hsv2rgb(vec4(hueValue, 1, 1, 1)), v_TexCoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,31 +1,25 @@
|
|||||||
#include "sh_Utils.h"
|
layout(location = 0) in highp vec2 m_Position;
|
||||||
|
layout(location = 1) in lowp vec4 m_Colour;
|
||||||
|
layout(location = 2) in highp vec2 m_TexCoord;
|
||||||
|
layout(location = 3) in highp vec4 m_TexRect;
|
||||||
|
layout(location = 4) in mediump vec2 m_BlendRange;
|
||||||
|
|
||||||
attribute highp vec2 m_Position;
|
layout(location = 0) out highp vec2 v_MaskingPosition;
|
||||||
attribute lowp vec4 m_Colour;
|
layout(location = 1) out lowp vec4 v_Colour;
|
||||||
attribute mediump vec2 m_TexCoord;
|
layout(location = 2) out highp vec2 v_TexCoord;
|
||||||
attribute mediump vec4 m_TexRect;
|
layout(location = 3) out highp vec4 v_TexRect;
|
||||||
attribute mediump vec2 m_BlendRange;
|
layout(location = 4) out mediump vec2 v_BlendRange;
|
||||||
|
|
||||||
varying highp vec2 v_MaskingPosition;
|
|
||||||
varying lowp vec4 v_Colour;
|
|
||||||
varying mediump vec2 v_TexCoord;
|
|
||||||
varying mediump vec4 v_TexRect;
|
|
||||||
varying mediump vec2 v_BlendRange;
|
|
||||||
|
|
||||||
uniform highp mat4 g_ProjMatrix;
|
|
||||||
uniform highp mat3 g_ToMaskingSpace;
|
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
// Transform from screen space to masking space.
|
// Transform from screen space to masking space.
|
||||||
highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0);
|
highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0);
|
||||||
v_MaskingPosition = maskingPos.xy / maskingPos.z;
|
v_MaskingPosition = maskingPos.xy / maskingPos.z;
|
||||||
|
|
||||||
v_Colour = m_Colour;
|
v_Colour = m_Colour;
|
||||||
v_TexCoord = m_TexCoord;
|
v_TexCoord = m_TexCoord;
|
||||||
v_TexRect = m_TexRect;
|
v_TexRect = m_TexRect;
|
||||||
v_BlendRange = m_BlendRange;
|
v_BlendRange = m_BlendRange;
|
||||||
|
|
||||||
gl_Position = gProjMatrix * vec4(m_Position, 1.0, 1.0);
|
gl_Position = g_ProjMatrix * vec4(m_Position, 1.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
[Events]
|
||||||
|
//Storyboard Layer 0 (Background)
|
||||||
|
Animation,Background,Centre,"img.jpg",320,240,2,150,LoopForever
|
||||||
|
F,0,2000,,0,1
|
||||||
|
L,2000,10
|
||||||
|
F,18,0,1000,1,0
|
4
osu.Game.Tests/Resources/image-specified-as-video.osb
Normal file
4
osu.Game.Tests/Resources/image-specified-as-video.osb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[Events]
|
||||||
|
Video,0,"BG.jpg",0,0
|
@ -0,0 +1,5 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[Events]
|
||||||
|
0,0,"BG.jpg",0,0
|
||||||
|
Video,0,"Video.avi",0,0
|
@ -0,0 +1,5 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[Events]
|
||||||
|
0,0,"BG.jpg",0,0
|
||||||
|
Video,0,"Video.AVI",0,0
|
@ -164,7 +164,7 @@ namespace osu.Game.Tests.Rulesets
|
|||||||
this.parentManager = parentManager;
|
this.parentManager = parentManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override byte[] LoadRaw(string name) => parentManager.LoadRaw(name);
|
public override byte[] GetRawData(string fileName) => parentManager.GetRawData(fileName);
|
||||||
|
|
||||||
public bool IsDisposed { get; private set; }
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -51,9 +49,13 @@ namespace osu.Game.Tests.Testing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestRetrieveShader()
|
public void TestRetrieveShader()
|
||||||
{
|
{
|
||||||
AddAssert("ruleset shaders retrieved", () =>
|
AddStep("ruleset shaders retrieved without error", () =>
|
||||||
Dependencies.Get<ShaderManager>().LoadRaw(@"sh_TestVertex.vs") != null &&
|
{
|
||||||
Dependencies.Get<ShaderManager>().LoadRaw(@"sh_TestFragment.fs") != null);
|
Dependencies.Get<ShaderManager>().GetRawData(@"sh_TestVertex.vs");
|
||||||
|
Dependencies.Get<ShaderManager>().GetRawData(@"sh_TestFragment.fs");
|
||||||
|
Dependencies.Get<ShaderManager>().Load(@"TestVertex", @"TestFragment");
|
||||||
|
Dependencies.Get<ShaderManager>().Load(VertexShaderDescriptor.TEXTURE_2, @"TestFragment");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -76,12 +78,12 @@ namespace osu.Game.Tests.Testing
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override IResourceStore<byte[]> CreateResourceStore() => new NamespacedResourceStore<byte[]>(TestResources.GetStore(), @"Resources");
|
public override IResourceStore<byte[]> CreateResourceStore() => new NamespacedResourceStore<byte[]>(TestResources.GetStore(), @"Resources");
|
||||||
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new TestRulesetConfigManager();
|
public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new TestRulesetConfigManager();
|
||||||
|
|
||||||
public override IEnumerable<Mod> GetModsFor(ModType type) => Array.Empty<Mod>();
|
public override IEnumerable<Mod> GetModsFor(ModType type) => Array.Empty<Mod>();
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => null;
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod>? mods = null) => null!;
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => null;
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => null!;
|
||||||
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null;
|
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestRulesetConfigManager : IRulesetConfigManager
|
private class TestRulesetConfigManager : IRulesetConfigManager
|
||||||
|
@ -311,6 +311,7 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
public bool IsDrawable => true;
|
public bool IsDrawable => true;
|
||||||
public double StartTime => double.MinValue;
|
public double StartTime => double.MinValue;
|
||||||
public double EndTime => double.MaxValue;
|
public double EndTime => double.MaxValue;
|
||||||
|
public double EndTimeForDisplay => double.MaxValue;
|
||||||
|
|
||||||
public Drawable CreateDrawable() => new DrawableTestStoryboardElement();
|
public Drawable CreateDrawable() => new DrawableTestStoryboardElement();
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
@ -95,10 +94,6 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
var path = slider.Path;
|
var path = slider.Path;
|
||||||
return path.ControlPoints.Count == 2 && path.ControlPoints.SequenceEqual(addedObject.Path.ControlPoints);
|
return path.ControlPoints.Count == 2 && path.ControlPoints.SequenceEqual(addedObject.Path.ControlPoints);
|
||||||
});
|
});
|
||||||
|
|
||||||
// see `HitObject.control_point_leniency`.
|
|
||||||
AddAssert("sample control point has correct time", () => Precision.AlmostEquals(slider.SampleControlPoint.Time, slider.GetEndTime(), 1));
|
|
||||||
AddAssert("difficulty control point has correct time", () => slider.DifficultyControlPoint.Time == slider.StartTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -122,19 +122,9 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddAssert("Beatmap has correct timing point", () => EditorBeatmap.ControlPointInfo.TimingPoints.Single().Time == 500);
|
AddAssert("Beatmap has correct timing point", () => EditorBeatmap.ControlPointInfo.TimingPoints.Single().Time == 500);
|
||||||
|
|
||||||
// After placement these must be non-default as defaults are read-only.
|
|
||||||
AddAssert("Placed object has non-default control points", () =>
|
|
||||||
!ReferenceEquals(EditorBeatmap.HitObjects[0].SampleControlPoint, SampleControlPoint.DEFAULT) &&
|
|
||||||
!ReferenceEquals(EditorBeatmap.HitObjects[0].DifficultyControlPoint, DifficultyControlPoint.DEFAULT));
|
|
||||||
|
|
||||||
ReloadEditorToSameBeatmap();
|
ReloadEditorToSameBeatmap();
|
||||||
|
|
||||||
AddAssert("Beatmap still has correct timing point", () => EditorBeatmap.ControlPointInfo.TimingPoints.Single().Time == 500);
|
AddAssert("Beatmap still has correct timing point", () => EditorBeatmap.ControlPointInfo.TimingPoints.Single().Time == 500);
|
||||||
|
|
||||||
// After placement these must be non-default as defaults are read-only.
|
|
||||||
AddAssert("Placed object still has non-default control points", () =>
|
|
||||||
!ReferenceEquals(EditorBeatmap.HitObjects[0].SampleControlPoint, SampleControlPoint.DEFAULT) &&
|
|
||||||
!ReferenceEquals(EditorBeatmap.HitObjects[0].DifficultyControlPoint, DifficultyControlPoint.DEFAULT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -108,12 +108,16 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType<EditorRadioButton>().First(d => d.Button.Label == "HitCircle").TriggerClick());
|
AddStep("Change to hitcircle", () => hitObjectComposer.ChildrenOfType<EditorRadioButton>().First(d => d.Button.Label == "HitCircle").TriggerClick());
|
||||||
|
|
||||||
|
ExpandingToolboxContainer toolboxContainer = null!;
|
||||||
|
|
||||||
|
AddStep("move mouse to toolbox", () => InputManager.MoveMouseTo(toolboxContainer = hitObjectComposer.ChildrenOfType<ExpandingToolboxContainer>().First()));
|
||||||
|
AddUntilStep("toolbox is expanded", () => toolboxContainer.Expanded.Value);
|
||||||
|
AddUntilStep("wait for toolbox to expand", () => toolboxContainer.LatestTransformEndTime, () => Is.EqualTo(Time.Current));
|
||||||
|
|
||||||
AddStep("move mouse to overlapping toggle button", () =>
|
AddStep("move mouse to overlapping toggle button", () =>
|
||||||
{
|
{
|
||||||
var playfield = hitObjectComposer.Playfield.ScreenSpaceDrawQuad;
|
var playfield = hitObjectComposer.Playfield.ScreenSpaceDrawQuad;
|
||||||
var button = hitObjectComposer
|
var button = toolboxContainer.ChildrenOfType<DrawableTernaryButton>().First(b => playfield.Contains(b.ScreenSpaceDrawQuad.Centre));
|
||||||
.ChildrenOfType<ExpandingToolboxContainer>().First()
|
|
||||||
.ChildrenOfType<DrawableTernaryButton>().First(b => playfield.Contains(b.ScreenSpaceDrawQuad.Centre));
|
|
||||||
|
|
||||||
InputManager.MoveMouseTo(button);
|
InputManager.MoveMouseTo(button);
|
||||||
});
|
});
|
||||||
|
@ -8,10 +8,10 @@ using Humanizer;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
@ -61,10 +61,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
new PathControlPoint(new Vector2(100, 0))
|
new PathControlPoint(new Vector2(100, 0))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DifficultyControlPoint = new DifficultyControlPoint
|
SliderVelocity = 2
|
||||||
{
|
|
||||||
SliderVelocity = 2
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -100,8 +97,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
AddStep("unify slider velocity", () =>
|
AddStep("unify slider velocity", () =>
|
||||||
{
|
{
|
||||||
foreach (var h in EditorBeatmap.HitObjects)
|
foreach (var h in EditorBeatmap.HitObjects.OfType<IHasSliderVelocity>())
|
||||||
h.DifficultyControlPoint.SliderVelocity = 1.5;
|
h.SliderVelocity = 1.5;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
||||||
@ -185,7 +182,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
private void hitObjectHasVelocity(int objectIndex, double velocity) => AddAssert($"{objectIndex.ToOrdinalWords()} has velocity {velocity}", () =>
|
private void hitObjectHasVelocity(int objectIndex, double velocity) => AddAssert($"{objectIndex.ToOrdinalWords()} has velocity {velocity}", () =>
|
||||||
{
|
{
|
||||||
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);
|
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);
|
||||||
return h.DifficultyControlPoint.SliderVelocity == velocity;
|
return h is IHasSliderVelocity hasSliderVelocity && hasSliderVelocity.SliderVelocity == velocity;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -39,10 +40,9 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
StartTime = 0,
|
StartTime = 0,
|
||||||
Position = (OsuPlayfield.BASE_SIZE - new Vector2(100, 0)) / 2,
|
Position = (OsuPlayfield.BASE_SIZE - new Vector2(100, 0)) / 2,
|
||||||
SampleControlPoint = new SampleControlPoint
|
Samples = new List<HitSampleInfo>
|
||||||
{
|
{
|
||||||
SampleBank = "normal",
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 80)
|
||||||
SampleVolume = 80
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -50,10 +50,9 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
StartTime = 500,
|
StartTime = 500,
|
||||||
Position = (OsuPlayfield.BASE_SIZE + new Vector2(100, 0)) / 2,
|
Position = (OsuPlayfield.BASE_SIZE + new Vector2(100, 0)) / 2,
|
||||||
SampleControlPoint = new SampleControlPoint
|
Samples = new List<HitSampleInfo>
|
||||||
{
|
{
|
||||||
SampleBank = "soft",
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "soft", volume: 60)
|
||||||
SampleVolume = 60
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -96,7 +95,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("unify sample volume", () =>
|
AddStep("unify sample volume", () =>
|
||||||
{
|
{
|
||||||
foreach (var h in EditorBeatmap.HitObjects)
|
foreach (var h in EditorBeatmap.HitObjects)
|
||||||
h.SampleControlPoint.SampleVolume = 50;
|
{
|
||||||
|
for (int i = 0; i < h.Samples.Count; i++)
|
||||||
|
{
|
||||||
|
h.Samples[i] = h.Samples[i].With(newVolume: 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
||||||
@ -136,7 +140,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("unify sample bank", () =>
|
AddStep("unify sample bank", () =>
|
||||||
{
|
{
|
||||||
foreach (var h in EditorBeatmap.HitObjects)
|
foreach (var h in EditorBeatmap.HitObjects)
|
||||||
h.SampleControlPoint.SampleBank = "soft";
|
{
|
||||||
|
for (int i = 0; i < h.Samples.Count; i++)
|
||||||
|
{
|
||||||
|
h.Samples[i] = h.Samples[i].With(newBank: "soft");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
||||||
@ -248,7 +257,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
private void hitObjectHasSampleVolume(int objectIndex, int volume) => AddAssert($"{objectIndex.ToOrdinalWords()} has volume {volume}", () =>
|
private void hitObjectHasSampleVolume(int objectIndex, int volume) => AddAssert($"{objectIndex.ToOrdinalWords()} has volume {volume}", () =>
|
||||||
{
|
{
|
||||||
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);
|
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);
|
||||||
return h.SampleControlPoint.SampleVolume == volume;
|
return h.Samples.All(o => o.Volume == volume);
|
||||||
});
|
});
|
||||||
|
|
||||||
private void setBankViaPopover(string bank) => AddStep($"set bank {bank} via popover", () =>
|
private void setBankViaPopover(string bank) => AddStep($"set bank {bank} via popover", () =>
|
||||||
@ -265,7 +274,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
private void hitObjectHasSampleBank(int objectIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} has bank {bank}", () =>
|
private void hitObjectHasSampleBank(int objectIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} has bank {bank}", () =>
|
||||||
{
|
{
|
||||||
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);
|
var h = EditorBeatmap.HitObjects.ElementAt(objectIndex);
|
||||||
return h.SampleControlPoint.SampleBank == bank;
|
return h.Samples.All(o => o.Bank == bank);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Game.Database;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
|
{
|
||||||
|
public partial class TestSceneLocallyModifyingOnlineBeatmaps : EditorSavingTestScene
|
||||||
|
{
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
CreateInitialBeatmap = () =>
|
||||||
|
{
|
||||||
|
var importedSet = Game.BeatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).GetResultSafely();
|
||||||
|
return Game.BeatmapManager.GetWorkingBeatmap(importedSet!.Value.Beatmaps.First());
|
||||||
|
};
|
||||||
|
|
||||||
|
base.SetUpSteps();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLocallyModifyingOnlineBeatmap()
|
||||||
|
{
|
||||||
|
AddAssert("editor beatmap has online ID", () => EditorBeatmap.BeatmapInfo.OnlineID, () => Is.GreaterThan(0));
|
||||||
|
|
||||||
|
AddStep("delete first hitobject", () => EditorBeatmap.RemoveAt(0));
|
||||||
|
SaveEditor();
|
||||||
|
|
||||||
|
ReloadEditorToSameBeatmap();
|
||||||
|
AddAssert("editor beatmap online ID reset", () => EditorBeatmap.BeatmapInfo.OnlineID, () => Is.EqualTo(-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -77,5 +77,39 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddAssert("object has non-zero duration", () => EditorBeatmap.HitObjects.OfType<IHasDuration>().Single().Duration > 0);
|
AddAssert("object has non-zero duration", () => EditorBeatmap.HitObjects.OfType<IHasDuration>().Single().Duration > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDisallowRepeatsOnZeroDurationObjects()
|
||||||
|
{
|
||||||
|
DragArea dragArea;
|
||||||
|
|
||||||
|
AddStep("add zero length slider", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.Clear();
|
||||||
|
EditorBeatmap.Add(new Slider
|
||||||
|
{
|
||||||
|
Position = new Vector2(256, 256),
|
||||||
|
StartTime = 2700
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("hold down drag bar", () =>
|
||||||
|
{
|
||||||
|
// distinguishes between the actual drag bar and its "underlay shadow".
|
||||||
|
dragArea = this.ChildrenOfType<DragArea>().Single(bar => bar.HandlePositionalInput);
|
||||||
|
InputManager.MoveMouseTo(dragArea);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("try to extend drag bar", () =>
|
||||||
|
{
|
||||||
|
var blueprint = this.ChildrenOfType<TimelineHitObjectBlueprint>().Single();
|
||||||
|
InputManager.MoveMouseTo(blueprint.SelectionQuad.TopLeft + new Vector2(100, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("release button", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("object has zero repeats", () => EditorBeatmap.HitObjects.OfType<IHasRepeats>().Single().RepeatCount == 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,14 +35,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
var referenceBeatmap = CreateBeatmap(new OsuRuleset().RulesetInfo);
|
var referenceBeatmap = CreateBeatmap(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||||
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
|
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Counters.Any(kc => kc.CountPresses.Value > 2));
|
||||||
|
|
||||||
seekTo(referenceBeatmap.Breaks[0].StartTime);
|
seekTo(referenceBeatmap.Breaks[0].StartTime);
|
||||||
AddAssert("keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
|
AddAssert("keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting.Value);
|
||||||
AddAssert("overlay displays 100% accuracy", () => Player.BreakOverlay.ChildrenOfType<BreakInfo>().Single().AccuracyDisplay.Current.Value == 1);
|
AddAssert("overlay displays 100% accuracy", () => Player.BreakOverlay.ChildrenOfType<BreakInfo>().Single().AccuracyDisplay.Current.Value == 1);
|
||||||
|
|
||||||
AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
|
AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
|
||||||
AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
|
AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Counters.All(kc => kc.CountPresses.Value == 0));
|
||||||
|
|
||||||
seekTo(referenceBeatmap.HitObjects[^1].GetEndTime());
|
seekTo(referenceBeatmap.HitObjects[^1].GetEndTime());
|
||||||
AddUntilStep("results displayed", () => getResultsScreen()?.IsLoaded == true);
|
AddUntilStep("results displayed", () => getResultsScreen()?.IsLoaded == true);
|
||||||
|
@ -31,11 +31,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||||
addSeekStep(3000);
|
addSeekStep(3000);
|
||||||
AddAssert("all judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
|
AddAssert("all judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
|
||||||
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Select(kc => kc.CountPresses).Sum() == 15);
|
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Counters.Select(kc => kc.CountPresses.Value).Sum() == 15);
|
||||||
AddStep("clear results", () => Player.Results.Clear());
|
AddStep("clear results", () => Player.Results.Clear());
|
||||||
addSeekStep(0);
|
addSeekStep(0);
|
||||||
AddAssert("none judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
|
AddAssert("none judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
|
||||||
AddUntilStep("key counters reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
|
AddUntilStep("key counters reset", () => Player.HUDOverlay.KeyCounter.Counters.All(kc => kc.CountPresses.Value == 0));
|
||||||
AddAssert("no results triggered", () => Player.Results.Count == 0);
|
AddAssert("no results triggered", () => Player.Results.Count == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +73,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
new HitCircle
|
new HitCircle
|
||||||
{
|
{
|
||||||
StartTime = t += spacing,
|
StartTime = t += spacing,
|
||||||
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) },
|
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "soft") },
|
||||||
SampleControlPoint = new SampleControlPoint { SampleBank = "soft" },
|
|
||||||
},
|
},
|
||||||
new HitCircle
|
new HitCircle
|
||||||
{
|
{
|
||||||
@ -84,8 +83,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
StartTime = t += spacing,
|
StartTime = t += spacing,
|
||||||
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 }),
|
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 }),
|
||||||
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE) },
|
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE, "soft") },
|
||||||
SampleControlPoint = new SampleControlPoint { SampleBank = "soft" },
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
// best way to check without exposing.
|
// best way to check without exposing.
|
||||||
private Drawable hideTarget => hudOverlay.KeyCounter;
|
private Drawable hideTarget => hudOverlay.KeyCounter;
|
||||||
private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First();
|
private Drawable keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().Single();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());
|
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());
|
||||||
|
|
||||||
// Add any key just to display the key counter visually.
|
// Add any key just to display the key counter visually.
|
||||||
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));
|
hudOverlay.KeyCounter.Add(new KeyCounterKeyboardTrigger(Key.Space));
|
||||||
|
|
||||||
scoreProcessor.Combo.Value = 1;
|
scoreProcessor.Combo.Value = 1;
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -17,43 +19,63 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
public TestSceneKeyCounter()
|
public TestSceneKeyCounter()
|
||||||
{
|
{
|
||||||
KeyCounterKeyboard testCounter;
|
KeyCounterDisplay defaultDisplay = new DefaultKeyCounterDisplay
|
||||||
|
|
||||||
KeyCounterDisplay kc = new KeyCounterDisplay
|
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Children = new KeyCounter[]
|
Position = new Vector2(0, 72.7f)
|
||||||
{
|
|
||||||
testCounter = new KeyCounterKeyboard(Key.X),
|
|
||||||
new KeyCounterKeyboard(Key.X),
|
|
||||||
new KeyCounterMouse(MouseButton.Left),
|
|
||||||
new KeyCounterMouse(MouseButton.Right),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
KeyCounterDisplay argonDisplay = new ArgonKeyCounterDisplay
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Position = new Vector2(0, -72.7f)
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultDisplay.AddRange(new InputTrigger[]
|
||||||
|
{
|
||||||
|
new KeyCounterKeyboardTrigger(Key.X),
|
||||||
|
new KeyCounterKeyboardTrigger(Key.X),
|
||||||
|
new KeyCounterMouseTrigger(MouseButton.Left),
|
||||||
|
new KeyCounterMouseTrigger(MouseButton.Right),
|
||||||
|
});
|
||||||
|
|
||||||
|
argonDisplay.AddRange(new InputTrigger[]
|
||||||
|
{
|
||||||
|
new KeyCounterKeyboardTrigger(Key.X),
|
||||||
|
new KeyCounterKeyboardTrigger(Key.X),
|
||||||
|
new KeyCounterMouseTrigger(MouseButton.Left),
|
||||||
|
new KeyCounterMouseTrigger(MouseButton.Right),
|
||||||
|
});
|
||||||
|
|
||||||
|
var testCounter = (DefaultKeyCounter)defaultDisplay.Counters.First();
|
||||||
|
|
||||||
AddStep("Add random", () =>
|
AddStep("Add random", () =>
|
||||||
{
|
{
|
||||||
Key key = (Key)((int)Key.A + RNG.Next(26));
|
Key key = (Key)((int)Key.A + RNG.Next(26));
|
||||||
kc.Add(new KeyCounterKeyboard(key));
|
defaultDisplay.Add(new KeyCounterKeyboardTrigger(key));
|
||||||
|
argonDisplay.Add(new KeyCounterKeyboardTrigger(key));
|
||||||
});
|
});
|
||||||
|
|
||||||
Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key;
|
Key testKey = ((KeyCounterKeyboardTrigger)defaultDisplay.Counters.First().Trigger).Key;
|
||||||
|
|
||||||
void addPressKeyStep()
|
addPressKeyStep();
|
||||||
|
AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses.Value == 1);
|
||||||
|
addPressKeyStep();
|
||||||
|
AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses.Value == 2);
|
||||||
|
AddStep("Disable counting", () =>
|
||||||
{
|
{
|
||||||
AddStep($"Press {testKey} key", () => InputManager.Key(testKey));
|
argonDisplay.IsCounting.Value = false;
|
||||||
}
|
defaultDisplay.IsCounting.Value = false;
|
||||||
|
});
|
||||||
|
addPressKeyStep();
|
||||||
|
AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses.Value == 2);
|
||||||
|
|
||||||
addPressKeyStep();
|
Add(defaultDisplay);
|
||||||
AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 1);
|
Add(argonDisplay);
|
||||||
addPressKeyStep();
|
|
||||||
AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 2);
|
|
||||||
AddStep("Disable counting", () => testCounter.IsCounting = false);
|
|
||||||
addPressKeyStep();
|
|
||||||
AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses == 2);
|
|
||||||
|
|
||||||
Add(kc);
|
void addPressKeyStep() => AddStep($"Press {testKey} key", () => InputManager.Key(testKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private SessionStatics sessionStatics { get; set; }
|
private SessionStatics sessionStatics { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
[Cached(typeof(INotificationOverlay))]
|
[Cached(typeof(INotificationOverlay))]
|
||||||
private readonly NotificationOverlay notificationOverlay;
|
private readonly NotificationOverlay notificationOverlay;
|
||||||
|
|
||||||
@ -317,6 +320,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
saveVolumes();
|
saveVolumes();
|
||||||
setFullVolume();
|
setFullVolume();
|
||||||
|
|
||||||
|
AddStep("enable storyboards", () => config.SetValue(OsuSetting.ShowStoryboard, true));
|
||||||
AddStep("change epilepsy warning", () => epilepsyWarning = warning);
|
AddStep("change epilepsy warning", () => epilepsyWarning = warning);
|
||||||
AddStep("load dummy beatmap", () => resetPlayer(false));
|
AddStep("load dummy beatmap", () => resetPlayer(false));
|
||||||
|
|
||||||
@ -333,12 +337,30 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
restoreVolumes();
|
restoreVolumes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEpilepsyWarningWithDisabledStoryboard()
|
||||||
|
{
|
||||||
|
saveVolumes();
|
||||||
|
setFullVolume();
|
||||||
|
|
||||||
|
AddStep("disable storyboards", () => config.SetValue(OsuSetting.ShowStoryboard, false));
|
||||||
|
AddStep("change epilepsy warning", () => epilepsyWarning = true);
|
||||||
|
AddStep("load dummy beatmap", () => resetPlayer(false));
|
||||||
|
|
||||||
|
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
|
||||||
|
|
||||||
|
AddUntilStep("epilepsy warning absent", () => getWarning() == null);
|
||||||
|
|
||||||
|
restoreVolumes();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestEpilepsyWarningEarlyExit()
|
public void TestEpilepsyWarningEarlyExit()
|
||||||
{
|
{
|
||||||
saveVolumes();
|
saveVolumes();
|
||||||
setFullVolume();
|
setFullVolume();
|
||||||
|
|
||||||
|
AddStep("enable storyboards", () => config.SetValue(OsuSetting.ShowStoryboard, true));
|
||||||
AddStep("set epilepsy warning", () => epilepsyWarning = true);
|
AddStep("set epilepsy warning", () => epilepsyWarning = true);
|
||||||
AddStep("load dummy beatmap", () => resetPlayer(false));
|
AddStep("load dummy beatmap", () => resetPlayer(false));
|
||||||
|
|
||||||
@ -449,7 +471,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("click notification", () => notification.TriggerClick());
|
AddStep("click notification", () => notification.TriggerClick());
|
||||||
}
|
}
|
||||||
|
|
||||||
private EpilepsyWarning getWarning() => loader.ChildrenOfType<EpilepsyWarning>().SingleOrDefault();
|
private EpilepsyWarning getWarning() => loader.ChildrenOfType<EpilepsyWarning>().SingleOrDefault(w => w.IsAlive);
|
||||||
|
|
||||||
private partial class TestPlayerLoader : PlayerLoader
|
private partial class TestPlayerLoader : PlayerLoader
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user