mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 21:52:55 +08:00
Merge branch 'master' into fix-load-too-fast
This commit is contained in:
commit
29f3e8e486
@ -1,7 +1,7 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="osu! SDL" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
|
<configuration default="false" name="osu! (legacy osuTK)" type="DotNetProject" factoryName=".NET Project" folderName="osu!" activateToolWindowBeforeRun="false">
|
||||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" />
|
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="--sdl" />
|
<option name="PROGRAM_PARAMETERS" value="--tk" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1" />
|
||||||
<option name="PASS_PARENT_ENVS" value="1" />
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
public float Position
|
public float Position
|
||||||
{
|
{
|
||||||
get => HitObject?.X ?? position;
|
get => HitObject?.EffectiveX ?? position;
|
||||||
set => position = value;
|
set => position = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
private void attemptCatch(Fruit fruit)
|
private void attemptCatch(Fruit fruit)
|
||||||
{
|
{
|
||||||
fruit.X += catcher.X;
|
fruit.X = fruit.OriginalX + catcher.X;
|
||||||
fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty
|
fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = circleSize
|
CircleSize = circleSize
|
||||||
|
@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
|
|
||||||
case JuiceStream juiceStream:
|
case JuiceStream juiceStream:
|
||||||
// Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead.
|
// Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead.
|
||||||
lastPosition = juiceStream.X + juiceStream.Path.ControlPoints[^1].Position.Value.X;
|
lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.Value.X;
|
||||||
|
|
||||||
// Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead.
|
// Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead.
|
||||||
lastStartTime = juiceStream.StartTime;
|
lastStartTime = juiceStream.StartTime;
|
||||||
@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
catchObject.XOffset = 0;
|
catchObject.XOffset = 0;
|
||||||
|
|
||||||
if (catchObject is TinyDroplet)
|
if (catchObject is TinyDroplet)
|
||||||
catchObject.XOffset = Math.Clamp(rng.Next(-20, 20), -catchObject.X, CatchPlayfield.WIDTH - catchObject.X);
|
catchObject.XOffset = Math.Clamp(rng.Next(-20, 20), -catchObject.OriginalX, CatchPlayfield.WIDTH - catchObject.OriginalX);
|
||||||
else if (catchObject is Droplet)
|
else if (catchObject is Droplet)
|
||||||
rng.Next(); // osu!stable retrieved a random droplet rotation
|
rng.Next(); // osu!stable retrieved a random droplet rotation
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
|
|
||||||
private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng)
|
private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng)
|
||||||
{
|
{
|
||||||
float offsetPosition = hitObject.X;
|
float offsetPosition = hitObject.OriginalX;
|
||||||
double startTime = hitObject.StartTime;
|
double startTime = hitObject.StartTime;
|
||||||
|
|
||||||
if (lastPosition == null)
|
if (lastPosition == null)
|
||||||
@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
if (positionDiff == 0)
|
if (positionDiff == 0)
|
||||||
{
|
{
|
||||||
applyRandomOffset(ref offsetPosition, timeDiff / 4d, rng);
|
applyRandomOffset(ref offsetPosition, timeDiff / 4d, rng);
|
||||||
hitObject.XOffset = offsetPosition - hitObject.X;
|
hitObject.XOffset = offsetPosition - hitObject.OriginalX;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
if (Math.Abs(positionDiff) < timeDiff / 3)
|
if (Math.Abs(positionDiff) < timeDiff / 3)
|
||||||
applyOffset(ref offsetPosition, positionDiff);
|
applyOffset(ref offsetPosition, positionDiff);
|
||||||
|
|
||||||
hitObject.XOffset = offsetPosition - hitObject.X;
|
hitObject.XOffset = offsetPosition - hitObject.OriginalX;
|
||||||
|
|
||||||
lastPosition = offsetPosition;
|
lastPosition = offsetPosition;
|
||||||
lastStartTime = startTime;
|
lastStartTime = startTime;
|
||||||
@ -230,9 +230,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
currentObject.HyperDashTarget = null;
|
currentObject.HyperDashTarget = null;
|
||||||
currentObject.DistanceToHyperDash = 0;
|
currentObject.DistanceToHyperDash = 0;
|
||||||
|
|
||||||
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
|
int thisDirection = nextObject.EffectiveX > currentObject.EffectiveX ? 1 : -1;
|
||||||
double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
|
double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
|
||||||
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
double distanceToNext = Math.Abs(nextObject.EffectiveX - currentObject.EffectiveX) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
||||||
float distanceToHyper = (float)(timeToNext * Catcher.BASE_SPEED - distanceToNext);
|
float distanceToHyper = (float)(timeToNext * Catcher.BASE_SPEED - distanceToNext);
|
||||||
|
|
||||||
if (distanceToHyper < 0)
|
if (distanceToHyper < 0)
|
||||||
|
@ -32,8 +32,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
|
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
|
||||||
var scalingFactor = normalized_hitobject_radius / halfCatcherWidth;
|
var scalingFactor = normalized_hitobject_radius / halfCatcherWidth;
|
||||||
|
|
||||||
NormalizedPosition = BaseObject.X * scalingFactor;
|
NormalizedPosition = BaseObject.EffectiveX * scalingFactor;
|
||||||
LastNormalizedPosition = LastObject.X * scalingFactor;
|
LastNormalizedPosition = LastObject.EffectiveX * scalingFactor;
|
||||||
|
|
||||||
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
||||||
StrainTime = Math.Max(40, DeltaTime);
|
StrainTime = Math.Max(40, DeltaTime);
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
@ -16,38 +15,46 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
public const float OBJECT_RADIUS = 64;
|
public const float OBJECT_RADIUS = 64;
|
||||||
|
|
||||||
// This value is after XOffset applied.
|
public readonly Bindable<float> OriginalXBindable = new Bindable<float>();
|
||||||
public readonly Bindable<float> XBindable = new Bindable<float>();
|
|
||||||
|
|
||||||
// This value is before XOffset applied.
|
|
||||||
private float originalX;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The horizontal position of the fruit between 0 and <see cref="CatchPlayfield.WIDTH"/>.
|
/// The horizontal position of the hit object between 0 and <see cref="CatchPlayfield.WIDTH"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float X
|
public float X
|
||||||
{
|
{
|
||||||
// TODO: I don't like this asymmetry.
|
set => OriginalXBindable.Value = value;
|
||||||
get => XBindable.Value;
|
|
||||||
// originalX is set by `XBindable.BindValueChanged`
|
|
||||||
set => XBindable.Value = value + xOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private float xOffset;
|
float IHasXPosition.X => OriginalXBindable.Value;
|
||||||
|
|
||||||
|
public readonly Bindable<float> XOffsetBindable = new Bindable<float>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A random offset applied to <see cref="X"/>, set by the <see cref="CatchBeatmapProcessor"/>.
|
/// A random offset applied to the horizontal position, set by the beatmap processing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal float XOffset
|
public float XOffset
|
||||||
{
|
{
|
||||||
get => xOffset;
|
set => XOffsetBindable.Value = value;
|
||||||
set
|
|
||||||
{
|
|
||||||
xOffset = value;
|
|
||||||
XBindable.Value = originalX + xOffset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The horizontal position of the hit object between 0 and <see cref="CatchPlayfield.WIDTH"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This value is the original <see cref="X"/> value specified in the beatmap, not affected by the beatmap processing.
|
||||||
|
/// Use <see cref="EffectiveX"/> for a gameplay.
|
||||||
|
/// </remarks>
|
||||||
|
public float OriginalX => OriginalXBindable.Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The effective horizontal position of the hit object between 0 and <see cref="CatchPlayfield.WIDTH"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This value is the original <see cref="X"/> value plus the offset applied by the beatmap processing.
|
||||||
|
/// Use <see cref="OriginalX"/> if a value not affected by the offset is desired.
|
||||||
|
/// </remarks>
|
||||||
|
public float EffectiveX => OriginalXBindable.Value + XOffsetBindable.Value;
|
||||||
|
|
||||||
public double TimePreempt = 1000;
|
public double TimePreempt = 1000;
|
||||||
|
|
||||||
public readonly Bindable<int> IndexInBeatmapBindable = new Bindable<int>();
|
public readonly Bindable<int> IndexInBeatmapBindable = new Bindable<int>();
|
||||||
@ -113,10 +120,5 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||||
|
|
||||||
protected CatchHitObject()
|
|
||||||
{
|
|
||||||
XBindable.BindValueChanged(x => originalX = x.NewValue - xOffset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
|
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
|
||||||
{
|
{
|
||||||
public readonly Bindable<float> XBindable = new Bindable<float>();
|
public readonly Bindable<float> OriginalXBindable = new Bindable<float>();
|
||||||
|
public readonly Bindable<float> XOffsetBindable = new Bindable<float>();
|
||||||
|
|
||||||
protected override double InitialLifetimeOffset => HitObject.TimePreempt;
|
protected override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||||
|
|
||||||
protected override float SamplePlaybackPosition => HitObject.X / CatchPlayfield.WIDTH;
|
protected override float SamplePlaybackPosition => HitObject.EffectiveX / CatchPlayfield.WIDTH;
|
||||||
|
|
||||||
public int RandomSeed => HitObject?.RandomSeed ?? 0;
|
public int RandomSeed => HitObject?.RandomSeed ?? 0;
|
||||||
|
|
||||||
@ -38,14 +39,16 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.OnApply();
|
base.OnApply();
|
||||||
|
|
||||||
XBindable.BindTo(HitObject.XBindable);
|
OriginalXBindable.BindTo(HitObject.OriginalXBindable);
|
||||||
|
XOffsetBindable.BindTo(HitObject.XOffsetBindable);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnFree()
|
protected override void OnFree()
|
||||||
{
|
{
|
||||||
base.OnFree();
|
base.OnFree();
|
||||||
|
|
||||||
XBindable.UnbindFrom(HitObject.XBindable);
|
OriginalXBindable.UnbindFrom(HitObject.OriginalXBindable);
|
||||||
|
XOffsetBindable.UnbindFrom(HitObject.XOffsetBindable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Func<CatchHitObject, bool> CheckPosition;
|
public Func<CatchHitObject, bool> CheckPosition;
|
||||||
|
@ -55,10 +55,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
XBindable.BindValueChanged(x =>
|
OriginalXBindable.BindValueChanged(updateXPosition);
|
||||||
{
|
XOffsetBindable.BindValueChanged(updateXPosition, true);
|
||||||
X = x.NewValue;
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
ScaleBindable.BindValueChanged(scale =>
|
ScaleBindable.BindValueChanged(scale =>
|
||||||
{
|
{
|
||||||
@ -69,6 +67,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
IndexInBeatmap.BindValueChanged(_ => UpdateComboColour());
|
IndexInBeatmap.BindValueChanged(_ => UpdateComboColour());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateXPosition(ValueChangedEvent<float> _)
|
||||||
|
{
|
||||||
|
X = OriginalXBindable.Value + XOffsetBindable.Value;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnApply()
|
protected override void OnApply()
|
||||||
{
|
{
|
||||||
base.OnApply();
|
base.OnApply();
|
||||||
|
@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
AddNested(new TinyDroplet
|
AddNested(new TinyDroplet
|
||||||
{
|
{
|
||||||
StartTime = t + lastEvent.Value.Time,
|
StartTime = t + lastEvent.Value.Time,
|
||||||
X = X + Path.PositionAt(
|
X = OriginalX + Path.PositionAt(
|
||||||
lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X,
|
lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
Samples = dropletSamples,
|
Samples = dropletSamples,
|
||||||
StartTime = e.Time,
|
StartTime = e.Time,
|
||||||
X = X + Path.PositionAt(e.PathProgress).X,
|
X = OriginalX + Path.PositionAt(e.PathProgress).X,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -104,14 +104,14 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
Samples = this.GetNodeSamples(nodeIndex++),
|
Samples = this.GetNodeSamples(nodeIndex++),
|
||||||
StartTime = e.Time,
|
StartTime = e.Time,
|
||||||
X = X + Path.PositionAt(e.PathProgress).X,
|
X = OriginalX + Path.PositionAt(e.PathProgress).X,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public float EndX => X + this.CurvePositionAt(1).X;
|
public float EndX => OriginalX + this.CurvePositionAt(1).X;
|
||||||
|
|
||||||
public double Duration
|
public double Duration
|
||||||
{
|
{
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
|
|
||||||
void moveToNext(PalpableCatchHitObject h)
|
void moveToNext(PalpableCatchHitObject h)
|
||||||
{
|
{
|
||||||
float positionChange = Math.Abs(lastPosition - h.X);
|
float positionChange = Math.Abs(lastPosition - h.EffectiveX);
|
||||||
double timeAvailable = h.StartTime - lastTime;
|
double timeAvailable = h.StartTime - lastTime;
|
||||||
|
|
||||||
// So we can either make it there without a dash or not.
|
// So we can either make it there without a dash or not.
|
||||||
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
// todo: get correct catcher size, based on difficulty CS.
|
// todo: get correct catcher size, based on difficulty CS.
|
||||||
const float catcher_width_half = CatcherArea.CATCHER_SIZE * 0.3f * 0.5f;
|
const float catcher_width_half = CatcherArea.CATCHER_SIZE * 0.3f * 0.5f;
|
||||||
|
|
||||||
if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X)
|
if (lastPosition - catcher_width_half < h.EffectiveX && lastPosition + catcher_width_half > h.EffectiveX)
|
||||||
{
|
{
|
||||||
// we are already in the correct range.
|
// we are already in the correct range.
|
||||||
lastTime = h.StartTime;
|
lastTime = h.StartTime;
|
||||||
@ -66,12 +66,12 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
|
|
||||||
if (impossibleJump)
|
if (impossibleJump)
|
||||||
{
|
{
|
||||||
addFrame(h.StartTime, h.X);
|
addFrame(h.StartTime, h.EffectiveX);
|
||||||
}
|
}
|
||||||
else if (h.HyperDash)
|
else if (h.HyperDash)
|
||||||
{
|
{
|
||||||
addFrame(h.StartTime - timeAvailable, lastPosition);
|
addFrame(h.StartTime - timeAvailable, lastPosition);
|
||||||
addFrame(h.StartTime, h.X);
|
addFrame(h.StartTime, h.EffectiveX);
|
||||||
}
|
}
|
||||||
else if (dashRequired)
|
else if (dashRequired)
|
||||||
{
|
{
|
||||||
@ -80,23 +80,23 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
||||||
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
||||||
|
|
||||||
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
|
float midPosition = (float)Interpolation.Lerp(lastPosition, h.EffectiveX, (float)timeAtDashSpeed / timeAvailable);
|
||||||
|
|
||||||
// dash movement
|
// dash movement
|
||||||
addFrame(h.StartTime - timeAvailable + 1, lastPosition, true);
|
addFrame(h.StartTime - timeAvailable + 1, lastPosition, true);
|
||||||
addFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition);
|
addFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition);
|
||||||
addFrame(h.StartTime, h.X);
|
addFrame(h.StartTime, h.EffectiveX);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double timeBefore = positionChange / movement_speed;
|
double timeBefore = positionChange / movement_speed;
|
||||||
|
|
||||||
addFrame(h.StartTime - timeBefore, lastPosition);
|
addFrame(h.StartTime - timeBefore, lastPosition);
|
||||||
addFrame(h.StartTime, h.X);
|
addFrame(h.StartTime, h.EffectiveX);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastTime = h.StartTime;
|
lastTime = h.StartTime;
|
||||||
lastPosition = h.X;
|
lastPosition = h.EffectiveX;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var obj in Beatmap.HitObjects)
|
foreach (var obj in Beatmap.HitObjects)
|
||||||
|
@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
var halfCatchWidth = catchWidth * 0.5f;
|
var halfCatchWidth = catchWidth * 0.5f;
|
||||||
|
|
||||||
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
|
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
|
||||||
var catchObjectPosition = fruit.X;
|
var catchObjectPosition = fruit.EffectiveX;
|
||||||
var catcherPosition = Position.X;
|
var catcherPosition = Position.X;
|
||||||
|
|
||||||
return catchObjectPosition >= catcherPosition - halfCatchWidth &&
|
return catchObjectPosition >= catcherPosition - halfCatchWidth &&
|
||||||
@ -250,10 +250,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
{
|
{
|
||||||
var target = hitObject.HyperDashTarget;
|
var target = hitObject.HyperDashTarget;
|
||||||
var timeDifference = target.StartTime - hitObject.StartTime;
|
var timeDifference = target.StartTime - hitObject.StartTime;
|
||||||
double positionDifference = target.X - X;
|
double positionDifference = target.EffectiveX - X;
|
||||||
var velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
|
var velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
|
||||||
|
|
||||||
SetHyperDashState(Math.Abs(velocity), target.X);
|
SetHyperDashState(Math.Abs(velocity), target.EffectiveX);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
SetHyperDashState();
|
SetHyperDashState();
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Animations;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||||
|
{
|
||||||
|
public class LegacyManiaJudgementPiece : CompositeDrawable, IAnimatableJudgement
|
||||||
|
{
|
||||||
|
private readonly HitResult result;
|
||||||
|
private readonly Drawable animation;
|
||||||
|
|
||||||
|
public LegacyManiaJudgementPiece(HitResult result, Drawable animation)
|
||||||
|
{
|
||||||
|
this.result = result;
|
||||||
|
this.animation = animation;
|
||||||
|
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
float? scorePosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.ScorePosition)?.Value;
|
||||||
|
|
||||||
|
if (scorePosition != null)
|
||||||
|
scorePosition -= Stage.HIT_TARGET_POSITION + 150;
|
||||||
|
|
||||||
|
Y = scorePosition ?? 0;
|
||||||
|
|
||||||
|
if (animation != null)
|
||||||
|
{
|
||||||
|
InternalChild = animation.With(d =>
|
||||||
|
{
|
||||||
|
d.Anchor = Anchor.Centre;
|
||||||
|
d.Origin = Anchor.Centre;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PlayAnimation()
|
||||||
|
{
|
||||||
|
if (animation == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
(animation as IFramedAnimation)?.GotoFrame(0);
|
||||||
|
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case HitResult.None:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HitResult.Miss:
|
||||||
|
animation.ScaleTo(1.6f);
|
||||||
|
animation.ScaleTo(1, 100, Easing.In);
|
||||||
|
|
||||||
|
animation.MoveTo(Vector2.Zero);
|
||||||
|
animation.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint);
|
||||||
|
|
||||||
|
animation.RotateTo(0);
|
||||||
|
animation.RotateTo(40, 800, Easing.InQuint);
|
||||||
|
|
||||||
|
this.FadeOutFromOne(800);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
animation.ScaleTo(0.8f);
|
||||||
|
animation.ScaleTo(1, 250, Easing.OutElastic);
|
||||||
|
|
||||||
|
animation.Delay(50).ScaleTo(0.75f, 250);
|
||||||
|
|
||||||
|
this.Delay(50).FadeOut(200);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Drawable GetAboveHitObjectsProxiedContent() => null;
|
||||||
|
}
|
||||||
|
}
|
@ -136,7 +136,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
string filename = this.GetManiaSkinConfig<string>(hitresult_mapping[result])?.Value
|
string filename = this.GetManiaSkinConfig<string>(hitresult_mapping[result])?.Value
|
||||||
?? default_hitresult_skin_filenames[result];
|
?? default_hitresult_skin_filenames[result];
|
||||||
|
|
||||||
return this.GetAnimation(filename, true, true);
|
var animation = this.GetAnimation(filename, true, true);
|
||||||
|
return animation == null ? null : new LegacyManiaJudgementPiece(result, animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SampleChannel GetSample(ISampleInfo sampleInfo)
|
public override SampleChannel GetSample(ISampleInfo sampleInfo)
|
||||||
|
@ -5,7 +5,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
@ -20,37 +19,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyMissAnimations()
|
|
||||||
{
|
|
||||||
if (!(JudgementBody.Drawable is DefaultManiaJudgementPiece))
|
|
||||||
{
|
|
||||||
// this is temporary logic until mania's skin transformer returns IAnimatableJudgements
|
|
||||||
JudgementBody.ScaleTo(1.6f);
|
|
||||||
JudgementBody.ScaleTo(1, 100, Easing.In);
|
|
||||||
|
|
||||||
JudgementBody.MoveTo(Vector2.Zero);
|
|
||||||
JudgementBody.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint);
|
|
||||||
|
|
||||||
JudgementBody.RotateTo(0);
|
|
||||||
JudgementBody.RotateTo(40, 800, Easing.InQuint);
|
|
||||||
JudgementBody.FadeOutFromOne(800);
|
|
||||||
|
|
||||||
LifetimeEnd = JudgementBody.LatestTransformEndTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
base.ApplyMissAnimations();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ApplyHitAnimations()
|
|
||||||
{
|
|
||||||
JudgementBody.ScaleTo(0.8f);
|
|
||||||
JudgementBody.ScaleTo(1, 250, Easing.OutElastic);
|
|
||||||
|
|
||||||
JudgementBody.Delay(50)
|
|
||||||
.ScaleTo(0.75f, 250)
|
|
||||||
.FadeOut(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Drawable CreateDefaultJudgement(HitResult result) => new DefaultManiaJudgementPiece(result);
|
protected override Drawable CreateDefaultJudgement(HitResult result) => new DefaultManiaJudgementPiece(result);
|
||||||
|
|
||||||
private class DefaultManiaJudgementPiece : DefaultJudgementPiece
|
private class DefaultManiaJudgementPiece : DefaultJudgementPiece
|
||||||
@ -66,6 +34,27 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
JudgementText.Font = JudgementText.Font.With(size: 25);
|
JudgementText.Font = JudgementText.Font.With(size: 25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void PlayAnimation()
|
||||||
|
{
|
||||||
|
base.PlayAnimation();
|
||||||
|
|
||||||
|
switch (Result)
|
||||||
|
{
|
||||||
|
case HitResult.None:
|
||||||
|
case HitResult.Miss:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.ScaleTo(0.8f);
|
||||||
|
this.ScaleTo(1, 250, Easing.OutElastic);
|
||||||
|
|
||||||
|
this.Delay(50)
|
||||||
|
.ScaleTo(0.75f, 250)
|
||||||
|
.FadeOut(200);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Y = HIT_TARGET_POSITION + 150,
|
Y = HIT_TARGET_POSITION + 150
|
||||||
},
|
},
|
||||||
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
}
|
}
|
||||||
|
@ -92,8 +92,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
aimValue *= lengthBonus;
|
aimValue *= lengthBonus;
|
||||||
|
|
||||||
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
||||||
aimValue *= Math.Pow(0.97, countMiss);
|
if (countMiss > 0)
|
||||||
|
aimValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), countMiss);
|
||||||
|
|
||||||
// Combo scaling
|
// Combo scaling
|
||||||
if (Attributes.MaxCombo > 0)
|
if (Attributes.MaxCombo > 0)
|
||||||
@ -138,8 +139,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
||||||
speedValue *= lengthBonus;
|
speedValue *= lengthBonus;
|
||||||
|
|
||||||
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
||||||
speedValue *= Math.Pow(0.97, countMiss);
|
if (countMiss > 0)
|
||||||
|
speedValue *= 0.97 * Math.Pow(1 - Math.Pow((double)countMiss / totalHits, 0.775), Math.Pow(countMiss, .875));
|
||||||
|
|
||||||
// Combo scaling
|
// Combo scaling
|
||||||
if (Attributes.MaxCombo > 0)
|
if (Attributes.MaxCombo > 0)
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
|
{
|
||||||
|
public abstract class HitObjectApplicationTestScene : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached(typeof(IScrollingInfo))]
|
||||||
|
private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo
|
||||||
|
{
|
||||||
|
Direction = { Value = ScrollingDirection.Left },
|
||||||
|
TimeRange = { Value = 1000 },
|
||||||
|
};
|
||||||
|
|
||||||
|
private ScrollingHitObjectContainer hitObjectContainer;
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUp()
|
||||||
|
=> AddStep("create SHOC", () => Child = hitObjectContainer = new ScrollingHitObjectContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 200,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Clock = new FramedClock(new StopwatchClock())
|
||||||
|
});
|
||||||
|
|
||||||
|
protected void AddHitObject(DrawableHitObject hitObject)
|
||||||
|
=> AddStep("add to SHOC", () => hitObjectContainer.Add(hitObject));
|
||||||
|
|
||||||
|
protected void RemoveHitObject(DrawableHitObject hitObject)
|
||||||
|
=> AddStep("remove from SHOC", () => hitObjectContainer.Remove(hitObject));
|
||||||
|
|
||||||
|
protected TObject PrepareObject<TObject>(TObject hitObject)
|
||||||
|
where TObject : TaikoHitObject
|
||||||
|
{
|
||||||
|
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
return hitObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
hoc.Add(new DrawableBarLineMajor(createBarLineAtCurrentTime(true))
|
hoc.Add(new DrawableBarLine(createBarLineAtCurrentTime(true))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
32
osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineApplication.cs
Normal file
32
osu.Game.Rulesets.Taiko.Tests/TestSceneBarLineApplication.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// 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.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneBarLineApplication : HitObjectApplicationTestScene
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestApplyNewBarLine()
|
||||||
|
{
|
||||||
|
DrawableBarLine barLine = new DrawableBarLine(PrepareObject(new BarLine
|
||||||
|
{
|
||||||
|
StartTime = 400,
|
||||||
|
Major = true
|
||||||
|
}));
|
||||||
|
|
||||||
|
AddHitObject(barLine);
|
||||||
|
RemoveHitObject(barLine);
|
||||||
|
|
||||||
|
AddStep("apply new bar line", () => barLine.Apply(PrepareObject(new BarLine
|
||||||
|
{
|
||||||
|
StartTime = 200,
|
||||||
|
Major = false
|
||||||
|
}), null));
|
||||||
|
AddHitObject(barLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -145,9 +145,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
private void addBarLine(bool major, double delay = scroll_time)
|
private void addBarLine(bool major, double delay = scroll_time)
|
||||||
{
|
{
|
||||||
BarLine bl = new BarLine { StartTime = DrawableRuleset.Playfield.Time.Current + delay };
|
BarLine bl = new BarLine
|
||||||
|
{
|
||||||
|
StartTime = DrawableRuleset.Playfield.Time.Current + delay,
|
||||||
|
Major = major
|
||||||
|
};
|
||||||
|
|
||||||
DrawableRuleset.Playfield.Add(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
|
DrawableRuleset.Playfield.Add(bl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSwell(double duration = default_duration)
|
private void addSwell(double duration = default_duration)
|
||||||
|
@ -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 osu.Framework.Bindables;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
@ -8,7 +9,13 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
{
|
{
|
||||||
public class BarLine : TaikoHitObject, IBarLine
|
public class BarLine : TaikoHitObject, IBarLine
|
||||||
{
|
{
|
||||||
public bool Major { get; set; }
|
public bool Major
|
||||||
|
{
|
||||||
|
get => MajorBindable.Value;
|
||||||
|
set => MajorBindable.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Bindable<bool> MajorBindable = new BindableBool();
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
// 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 JetBrains.Annotations;
|
||||||
|
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.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -15,38 +19,57 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DrawableBarLine : DrawableHitObject<HitObject>
|
public class DrawableBarLine : DrawableHitObject<HitObject>
|
||||||
{
|
{
|
||||||
|
public new BarLine HitObject => (BarLine)base.HitObject;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The width of the line tracker.
|
/// The width of the line tracker.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float tracker_width = 2f;
|
private const float tracker_width = 2f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fade out time calibrated to a pre-empt of 1000ms.
|
/// The vertical offset of the triangles from the line tracker.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float base_fadeout_time = 100f;
|
private const float triangle_offset = 10f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the triangles.
|
||||||
|
/// </summary>
|
||||||
|
private const float triangle_size = 20f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The visual line tracker.
|
/// The visual line tracker.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected SkinnableDrawable Line;
|
private SkinnableDrawable line;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The bar line.
|
/// Container with triangles. Only visible for major lines.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly BarLine BarLine;
|
private Container triangleContainer;
|
||||||
|
|
||||||
public DrawableBarLine(BarLine barLine)
|
private readonly Bindable<bool> major = new Bindable<bool>();
|
||||||
|
|
||||||
|
public DrawableBarLine()
|
||||||
|
: this(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableBarLine([CanBeNull] BarLine barLine)
|
||||||
: base(barLine)
|
: base(barLine)
|
||||||
{
|
{
|
||||||
BarLine = barLine;
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
Anchor = Anchor.CentreLeft;
|
Anchor = Anchor.CentreLeft;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
Width = tracker_width;
|
Width = tracker_width;
|
||||||
|
|
||||||
AddInternal(Line = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.BarLine), _ => new Box
|
AddRangeInternal(new Drawable[]
|
||||||
|
{
|
||||||
|
line = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.BarLine), _ => new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
EdgeSmoothness = new Vector2(0.5f, 0),
|
EdgeSmoothness = new Vector2(0.5f, 0),
|
||||||
@ -54,10 +77,65 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Alpha = 0.75f,
|
},
|
||||||
|
triangleContainer = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new EquilateralTriangle
|
||||||
|
{
|
||||||
|
Name = "Top",
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Position = new Vector2(0, -triangle_offset),
|
||||||
|
Size = new Vector2(-triangle_size),
|
||||||
|
EdgeSmoothness = new Vector2(1),
|
||||||
|
},
|
||||||
|
new EquilateralTriangle
|
||||||
|
{
|
||||||
|
Name = "Bottom",
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Position = new Vector2(0, triangle_offset),
|
||||||
|
Size = new Vector2(triangle_size),
|
||||||
|
EdgeSmoothness = new Vector2(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state) => this.FadeOut(150);
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
major.BindValueChanged(updateMajor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMajor(ValueChangedEvent<bool> major)
|
||||||
|
{
|
||||||
|
line.Alpha = major.NewValue ? 1f : 0.75f;
|
||||||
|
triangleContainer.Alpha = major.NewValue ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnApply()
|
||||||
|
{
|
||||||
|
base.OnApply();
|
||||||
|
major.BindTo(HitObject.MajorBindable);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFree()
|
||||||
|
{
|
||||||
|
base.OnFree();
|
||||||
|
major.UnbindFrom(HitObject.MajorBindable);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
{
|
||||||
|
using (BeginAbsoluteSequence(HitObject.StartTime))
|
||||||
|
this.FadeOutFromOne(150).Expire();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osuTK;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|
||||||
{
|
|
||||||
public class DrawableBarLineMajor : DrawableBarLine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The vertical offset of the triangles from the line tracker.
|
|
||||||
/// </summary>
|
|
||||||
private const float triangle_offfset = 10f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The size of the triangles.
|
|
||||||
/// </summary>
|
|
||||||
private const float triangle_size = 20f;
|
|
||||||
|
|
||||||
private readonly Container triangleContainer;
|
|
||||||
|
|
||||||
public DrawableBarLineMajor(BarLine barLine)
|
|
||||||
: base(barLine)
|
|
||||||
{
|
|
||||||
AddInternal(triangleContainer = new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new EquilateralTriangle
|
|
||||||
{
|
|
||||||
Name = "Top",
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
Position = new Vector2(0, -triangle_offfset),
|
|
||||||
Size = new Vector2(-triangle_size),
|
|
||||||
EdgeSmoothness = new Vector2(1),
|
|
||||||
},
|
|
||||||
new EquilateralTriangle
|
|
||||||
{
|
|
||||||
Name = "Bottom",
|
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
Position = new Vector2(0, triangle_offfset),
|
|
||||||
Size = new Vector2(triangle_size),
|
|
||||||
EdgeSmoothness = new Vector2(1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Line.Alpha = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
using (triangleContainer.BeginAbsoluteSequence(HitObject.StartTime))
|
|
||||||
triangleContainer.FadeOut(150);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
19
osu.Game.Rulesets.Taiko/UI/BarLinePlayfield.cs
Normal file
19
osu.Game.Rulesets.Taiko/UI/BarLinePlayfield.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 osu.Framework.Allocation;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
|
{
|
||||||
|
public class BarLinePlayfield : ScrollingPlayfield
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
RegisterPool<BarLine, DrawableBarLine>(15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
|
new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar));
|
||||||
|
|
||||||
FrameStableComponents.Add(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty())
|
FrameStableComponents.Add(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty())
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Beatmaps.ControlPoints;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
@ -38,10 +39,15 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
private SkinnableDrawable mascot;
|
private SkinnableDrawable mascot;
|
||||||
|
|
||||||
private ProxyContainer topLevelHitContainer;
|
private ProxyContainer topLevelHitContainer;
|
||||||
private ScrollingHitObjectContainer barlineContainer;
|
|
||||||
private Container rightArea;
|
private Container rightArea;
|
||||||
private Container leftArea;
|
private Container leftArea;
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// <see cref="Playfield.AddNested"/> is purposefully not called on this to prevent i.e. being able to interact
|
||||||
|
/// with bar lines in the editor.
|
||||||
|
/// </remarks>
|
||||||
|
private BarLinePlayfield barLinePlayfield;
|
||||||
|
|
||||||
private Container hitTargetOffsetContent;
|
private Container hitTargetOffsetContent;
|
||||||
|
|
||||||
public TaikoPlayfield(ControlPointInfo controlPoints)
|
public TaikoPlayfield(ControlPointInfo controlPoints)
|
||||||
@ -84,7 +90,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
barlineContainer = new ScrollingHitObjectContainer(),
|
barLinePlayfield = new BarLinePlayfield(),
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Hit objects",
|
Name = "Hit objects",
|
||||||
@ -155,12 +161,50 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
mascot.Scale = new Vector2(DrawHeight / DEFAULT_HEIGHT);
|
mascot.Scale = new Vector2(DrawHeight / DEFAULT_HEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Pooling support
|
||||||
|
|
||||||
|
public override void Add(HitObject h)
|
||||||
|
{
|
||||||
|
switch (h)
|
||||||
|
{
|
||||||
|
case BarLine barLine:
|
||||||
|
barLinePlayfield.Add(barLine);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TaikoHitObject taikoHitObject:
|
||||||
|
base.Add(taikoHitObject);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentException($"Unsupported {nameof(HitObject)} type: {h.GetType()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Remove(HitObject h)
|
||||||
|
{
|
||||||
|
switch (h)
|
||||||
|
{
|
||||||
|
case BarLine barLine:
|
||||||
|
return barLinePlayfield.Remove(barLine);
|
||||||
|
|
||||||
|
case TaikoHitObject taikoHitObject:
|
||||||
|
return base.Remove(taikoHitObject);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentException($"Unsupported {nameof(HitObject)} type: {h.GetType()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Non-pooling support
|
||||||
|
|
||||||
public override void Add(DrawableHitObject h)
|
public override void Add(DrawableHitObject h)
|
||||||
{
|
{
|
||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
case DrawableBarLine barline:
|
case DrawableBarLine barLine:
|
||||||
barlineContainer.Add(barline);
|
barLinePlayfield.Add(barLine);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DrawableTaikoHitObject taikoObject:
|
case DrawableTaikoHitObject taikoObject:
|
||||||
@ -170,7 +214,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unsupported {nameof(DrawableHitObject)} type");
|
throw new ArgumentException($"Unsupported {nameof(DrawableHitObject)} type: {h.GetType()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,8 +222,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
case DrawableBarLine barline:
|
case DrawableBarLine barLine:
|
||||||
return barlineContainer.Remove(barline);
|
return barLinePlayfield.Remove(barLine);
|
||||||
|
|
||||||
case DrawableTaikoHitObject _:
|
case DrawableTaikoHitObject _:
|
||||||
h.OnNewResult -= OnNewResult;
|
h.OnNewResult -= OnNewResult;
|
||||||
@ -187,10 +231,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
return base.Remove(h);
|
return base.Remove(h);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unsupported {nameof(DrawableHitObject)} type");
|
throw new ArgumentException($"Unsupported {nameof(DrawableHitObject)} type: {h.GetType()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||||
{
|
{
|
||||||
if (!DisplayJudgements.Value)
|
if (!DisplayJudgements.Value)
|
||||||
|
@ -191,7 +191,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Y = -separation,
|
Y = -separation,
|
||||||
HandleDrag = e => OnRotation?.Invoke(e.Delta.X),
|
HandleDrag = e => OnRotation?.Invoke(convertDragEventToAngleOfRotation(e)),
|
||||||
OperationStarted = operationStarted,
|
OperationStarted = operationStarted,
|
||||||
OperationEnded = operationEnded
|
OperationEnded = operationEnded
|
||||||
}
|
}
|
||||||
@ -242,6 +242,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
private int activeOperations;
|
private int activeOperations;
|
||||||
|
|
||||||
|
private float convertDragEventToAngleOfRotation(DragEvent e)
|
||||||
|
{
|
||||||
|
// Adjust coordinate system to the center of SelectionBox
|
||||||
|
float startAngle = MathF.Atan2(e.LastMousePosition.Y - DrawHeight / 2, e.LastMousePosition.X - DrawWidth / 2);
|
||||||
|
float endAngle = MathF.Atan2(e.MousePosition.Y - DrawHeight / 2, e.MousePosition.X - DrawWidth / 2);
|
||||||
|
|
||||||
|
return (endAngle - startAngle) * 180 / MathF.PI;
|
||||||
|
}
|
||||||
|
|
||||||
private void operationEnded()
|
private void operationEnded()
|
||||||
{
|
{
|
||||||
if (--activeOperations == 0)
|
if (--activeOperations == 0)
|
||||||
|
@ -35,6 +35,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public float HitPosition = (480 - 402) * POSITION_SCALE_FACTOR;
|
public float HitPosition = (480 - 402) * POSITION_SCALE_FACTOR;
|
||||||
public float LightPosition = (480 - 413) * POSITION_SCALE_FACTOR;
|
public float LightPosition = (480 - 413) * POSITION_SCALE_FACTOR;
|
||||||
|
public float ScorePosition = 300 * POSITION_SCALE_FACTOR;
|
||||||
public bool ShowJudgementLine = true;
|
public bool ShowJudgementLine = true;
|
||||||
public bool KeysUnderNotes;
|
public bool KeysUnderNotes;
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ namespace osu.Game.Skinning
|
|||||||
LeftLineWidth,
|
LeftLineWidth,
|
||||||
RightLineWidth,
|
RightLineWidth,
|
||||||
HitPosition,
|
HitPosition,
|
||||||
|
ScorePosition,
|
||||||
LightPosition,
|
LightPosition,
|
||||||
HitTargetImage,
|
HitTargetImage,
|
||||||
ShowJudgementLine,
|
ShowJudgementLine,
|
||||||
|
@ -94,6 +94,10 @@ namespace osu.Game.Skinning
|
|||||||
currentConfig.LightPosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
|
currentConfig.LightPosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "ScorePosition":
|
||||||
|
currentConfig.ScorePosition = (float.Parse(pair.Value, CultureInfo.InvariantCulture)) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
|
||||||
|
break;
|
||||||
|
|
||||||
case "JudgementLine":
|
case "JudgementLine":
|
||||||
currentConfig.ShowJudgementLine = pair.Value == "1";
|
currentConfig.ShowJudgementLine = pair.Value == "1";
|
||||||
break;
|
break;
|
||||||
|
@ -169,6 +169,9 @@ namespace osu.Game.Skinning
|
|||||||
case LegacyManiaSkinConfigurationLookups.HitPosition:
|
case LegacyManiaSkinConfigurationLookups.HitPosition:
|
||||||
return SkinUtils.As<TValue>(new Bindable<float>(existing.HitPosition));
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.HitPosition));
|
||||||
|
|
||||||
|
case LegacyManiaSkinConfigurationLookups.ScorePosition:
|
||||||
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.ScorePosition));
|
||||||
|
|
||||||
case LegacyManiaSkinConfigurationLookups.LightPosition:
|
case LegacyManiaSkinConfigurationLookups.LightPosition:
|
||||||
return SkinUtils.As<TValue>(new Bindable<float>(existing.LightPosition));
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.LightPosition));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user