mirror of
https://github.com/ppy/osu.git
synced 2026-05-15 07:02:36 +08:00
Compare commits
181 Commits
2018.626.0
...
2018.702.0
@@ -13,11 +13,12 @@ using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
internal class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
[TestFixture]
|
||||
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||
|
||||
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
|
||||
[TestCase("basic")]
|
||||
[TestCase("spinner")]
|
||||
[TestCase("spinner-and-circles")]
|
||||
public new void Test(string name)
|
||||
@@ -44,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
protected override Ruleset CreateRuleset() => new CatchRuleset();
|
||||
}
|
||||
|
||||
internal struct ConvertValue : IEquatable<ConvertValue>
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// A sane value to account for osu!stable using ints everwhere.
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
private DrawableFruit createDrawable(int index)
|
||||
{
|
||||
Fruit fruit = index == 5
|
||||
? new BananaShower.Banana
|
||||
? new Banana
|
||||
{
|
||||
StartTime = 1000000000000,
|
||||
IndexInBeatmap = index,
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
var positionData = obj as IHasXPosition;
|
||||
var comboData = obj as IHasCombo;
|
||||
var endTime = obj as IHasEndTime;
|
||||
var legacyOffset = obj as IHasLegacyLastTickOffset;
|
||||
|
||||
if (curveData != null)
|
||||
{
|
||||
@@ -39,7 +40,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
RepeatSamples = curveData.RepeatSamples,
|
||||
RepeatCount = curveData.RepeatCount,
|
||||
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
|
||||
NewCombo = comboData?.NewCombo ?? false
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset ?? 0
|
||||
};
|
||||
}
|
||||
else if (endTime != null)
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
public class CatchBeatmapProcessor : BeatmapProcessor
|
||||
{
|
||||
public const int RNG_SEED = 1337;
|
||||
|
||||
public CatchBeatmapProcessor(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
@@ -22,12 +24,12 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
|
||||
public override void PostProcess()
|
||||
{
|
||||
base.PostProcess();
|
||||
|
||||
applyPositionOffsets();
|
||||
|
||||
initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects);
|
||||
|
||||
base.PostProcess();
|
||||
|
||||
int index = 0;
|
||||
foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>())
|
||||
{
|
||||
@@ -37,8 +39,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
public const int RNG_SEED = 1337;
|
||||
|
||||
private void applyPositionOffsets()
|
||||
{
|
||||
var rng = new FastRandom(RNG_SEED);
|
||||
@@ -49,9 +49,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
switch (obj)
|
||||
{
|
||||
case BananaShower bananaShower:
|
||||
foreach (var nested in bananaShower.NestedHitObjects)
|
||||
foreach (var banana in bananaShower.NestedHitObjects.OfType<Banana>())
|
||||
{
|
||||
((BananaShower.Banana)nested).X = (float)rng.NextDouble();
|
||||
banana.X = (float)rng.NextDouble();
|
||||
rng.Next(); // osu!stable retrieved a random banana type
|
||||
rng.Next(); // osu!stable retrieved a random banana rotation
|
||||
rng.Next(); // osu!stable retrieved a random banana colour
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Judgements
|
||||
{
|
||||
public class CatchBananaJudgement : CatchJudgement
|
||||
{
|
||||
public override bool AffectsCombo => false;
|
||||
|
||||
public override bool ShouldExplode => true;
|
||||
|
||||
protected override int NumericResultFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case HitResult.Perfect:
|
||||
return 1100;
|
||||
}
|
||||
}
|
||||
|
||||
protected override float HealthIncreaseFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case HitResult.Perfect:
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Judgements
|
||||
{
|
||||
public class CatchDropletJudgement : CatchJudgement
|
||||
{
|
||||
protected override int NumericResultFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case HitResult.Perfect:
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
protected override float HealthIncreaseFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case HitResult.Perfect:
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,51 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Judgements
|
||||
{
|
||||
public class CatchJudgement : Judgement
|
||||
{
|
||||
// todo: wangs
|
||||
public override HitResult MaxResult => HitResult.Perfect;
|
||||
|
||||
protected override int NumericResultFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case HitResult.Perfect:
|
||||
return 300;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The base health increase for the result achieved.
|
||||
/// </summary>
|
||||
public float HealthIncrease => HealthIncreaseFor(Result);
|
||||
|
||||
/// <summary>
|
||||
/// Whether fruit on the platter should explode or drop.
|
||||
/// Note that this is only checked if the owning object is also <see cref="IHasComboInformation.LastInCombo" />
|
||||
/// </summary>
|
||||
public virtual bool ShouldExplode => IsHit;
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="HitResult"/> to a base health increase.
|
||||
/// </summary>
|
||||
/// <param name="result">The value to convert.</param>
|
||||
/// <returns>The base health increase.</returns>
|
||||
protected virtual float HealthIncreaseFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case HitResult.Perfect:
|
||||
return 10.2f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Judgements
|
||||
{
|
||||
public class CatchTinyDropletJudgement : CatchJudgement
|
||||
{
|
||||
public override bool AffectsCombo => false;
|
||||
|
||||
protected override int NumericResultFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case HitResult.Perfect:
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
|
||||
protected override float HealthIncreaseFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case HitResult.Perfect:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public class Banana : Fruit
|
||||
{
|
||||
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
||||
}
|
||||
}
|
||||
@@ -37,10 +37,5 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
public double EndTime => StartTime + Duration;
|
||||
|
||||
public double Duration { get; set; }
|
||||
|
||||
public class Banana : Fruit
|
||||
{
|
||||
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public class DrawableBanana : DrawableFruit
|
||||
{
|
||||
public DrawableBanana(Banana h)
|
||||
: base(h)
|
||||
{
|
||||
}
|
||||
|
||||
protected override CatchJudgement CreateJudgement() => new CatchBananaJudgement();
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,7 @@ using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
@@ -24,15 +22,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
|
||||
InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
foreach (var b in s.NestedHitObjects.Cast<BananaShower.Banana>())
|
||||
foreach (var b in s.NestedHitObjects.Cast<Banana>())
|
||||
AddNested(getVisualRepresentation?.Invoke(b));
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (timeOffset >= 0)
|
||||
AddJudgement(new Judgement { Result = NestedHitObjects.Cast<DrawableCatchHitObject>().Any(n => n.Judgements.Any(j => j.IsHit)) ? HitResult.Perfect : HitResult.Miss });
|
||||
}
|
||||
protected override bool ProvidesJudgement => false;
|
||||
|
||||
protected override void AddNested(DrawableHitObject h)
|
||||
{
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
@@ -58,9 +58,15 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
if (CheckPosition == null) return;
|
||||
|
||||
if (timeOffset >= 0)
|
||||
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
|
||||
{
|
||||
var judgement = CreateJudgement();
|
||||
judgement.Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss;
|
||||
AddJudgement(judgement);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual CatchJudgement CreateJudgement() => new CatchJudgement();
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
|
||||
@@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
@@ -23,6 +24,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
Masking = false;
|
||||
}
|
||||
|
||||
protected override CatchJudgement CreateJudgement() => new CatchDropletJudgement();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public class DrawableTinyDroplet : DrawableDroplet
|
||||
{
|
||||
public DrawableTinyDroplet(Droplet h)
|
||||
: base(h)
|
||||
{
|
||||
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 8;
|
||||
}
|
||||
|
||||
protected override CatchJudgement CreateJudgement() => new CatchTinyDropletJudgement();
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,13 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
double time = spanStartTime + timeProgress * spanDuration;
|
||||
|
||||
if (LegacyLastTickOffset != null)
|
||||
{
|
||||
// If we're the last tick, apply the legacy offset
|
||||
if (span == this.SpanCount() - 1 && d + tickDistance > length)
|
||||
time = Math.Max(StartTime + Duration / 2, time - LegacyLastTickOffset.Value);
|
||||
}
|
||||
|
||||
double tinyTickInterval = time - lastDropletTime;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
@@ -152,5 +159,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
get { return Curve.CurveType; }
|
||||
set { Curve.CurveType = value; }
|
||||
}
|
||||
|
||||
public double? LegacyLastTickOffset { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
return;
|
||||
}
|
||||
|
||||
if (h is BananaShower.Banana)
|
||||
if (h is Banana)
|
||||
{
|
||||
// auto bananas unrealistically warp to catch 100% combo.
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
@@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
switch (nestedObj)
|
||||
{
|
||||
case BananaShower.Banana _:
|
||||
case Banana _:
|
||||
case TinyDroplet _:
|
||||
case Droplet _:
|
||||
case Fruit _:
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
@@ -17,28 +19,57 @@ namespace osu.Game.Rulesets.Catch.Scoring
|
||||
{
|
||||
}
|
||||
|
||||
private float hpDrainRate;
|
||||
|
||||
protected override void SimulateAutoplay(Beatmap<CatchHitObject> beatmap)
|
||||
{
|
||||
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
|
||||
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case JuiceStream stream:
|
||||
foreach (var _ in stream.NestedHitObjects.Cast<CatchHitObject>())
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
foreach (var nestedObject in stream.NestedHitObjects)
|
||||
switch (nestedObject)
|
||||
{
|
||||
case TinyDroplet _:
|
||||
AddJudgement(new CatchTinyDropletJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
case Droplet _:
|
||||
AddJudgement(new CatchDropletJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
case Fruit _:
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case BananaShower shower:
|
||||
foreach (var _ in shower.NestedHitObjects.Cast<CatchHitObject>())
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
AddJudgement(new CatchBananaJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
case Fruit _:
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.SimulateAutoplay(beatmap);
|
||||
private const double harshness = 0.01;
|
||||
|
||||
protected override void OnNewJudgement(Judgement judgement)
|
||||
{
|
||||
base.OnNewJudgement(judgement);
|
||||
|
||||
if (judgement.Result == HitResult.Miss)
|
||||
{
|
||||
if (!judgement.IsBonus)
|
||||
Health.Value -= hpDrainRate * (harshness * 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (judgement is CatchJudgement catchJudgement)
|
||||
Health.Value += Math.Max(catchJudgement.HealthIncrease - hpDrainRate, 0) * harshness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,14 +38,16 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
switch (h)
|
||||
{
|
||||
case Banana banana:
|
||||
return new DrawableBanana(banana);
|
||||
case Fruit fruit:
|
||||
return new DrawableFruit(fruit);
|
||||
case JuiceStream stream:
|
||||
return new DrawableJuiceStream(stream, GetVisualRepresentation);
|
||||
case BananaShower banana:
|
||||
return new DrawableBananaShower(banana, GetVisualRepresentation);
|
||||
case BananaShower shower:
|
||||
return new DrawableBananaShower(shower, GetVisualRepresentation);
|
||||
case TinyDroplet tiny:
|
||||
return new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) };
|
||||
return new DrawableTinyDroplet(tiny);
|
||||
case Droplet droplet:
|
||||
return new DrawableDroplet(droplet);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
@@ -78,12 +79,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
if (!fruit.StaysOnPlate)
|
||||
runAfterLoaded(() => MovableCatcher.Explode(caughtFruit));
|
||||
|
||||
}
|
||||
|
||||
if (fruit.HitObject.LastInCombo)
|
||||
{
|
||||
if (judgement.IsHit)
|
||||
if (((CatchJudgement)judgement).ShouldExplode)
|
||||
runAfterLoaded(() => MovableCatcher.Explode());
|
||||
else
|
||||
MovableCatcher.Drop();
|
||||
|
||||
@@ -12,7 +12,8 @@ using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
internal class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
[TestFixture]
|
||||
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||
|
||||
@@ -36,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||
}
|
||||
|
||||
internal struct ConvertValue : IEquatable<ConvertValue>
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// A sane value to account for osu!stable using ints everwhere.
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
var obj = new Note { Column = i, StartTime = Time.Current + 2000 };
|
||||
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
columns[i].Add(new DrawableNote(obj, columns[i].Action));
|
||||
columns[i].Add(new DrawableNote(obj));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
var obj = new HoldNote { Column = i, StartTime = Time.Current + 2000, Duration = 500 };
|
||||
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
columns[i].Add(new DrawableHoldNote(obj, columns[i].Action));
|
||||
columns[i].Add(new DrawableHoldNote(obj));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Origin = Anchor.Centre,
|
||||
Height = 0.85f,
|
||||
AccentColour = Color4.OrangeRed,
|
||||
Action = action,
|
||||
Action = { Value = action },
|
||||
VisibleTimeRange = { Value = 2000 }
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -63,7 +64,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Child = new NoteContainer(direction, $"note, scrolling {direction.ToString().ToLower()}")
|
||||
{
|
||||
Child = new DrawableNote(note, ManiaAction.Key1) { AccentColour = Color4.OrangeRed }
|
||||
Child = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Child = new NoteContainer(direction, $"hold note, scrolling {direction.ToString().ToLower()}")
|
||||
{
|
||||
Child = new DrawableHoldNote(note, ManiaAction.Key1)
|
||||
Child = new DrawableHoldNote(note)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
AccentColour = Color4.OrangeRed,
|
||||
@@ -136,6 +137,13 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
};
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
dependencies.CacheAs<IBindable<ManiaAction>>(new Bindable<ManiaAction>());
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
var obj = new Note { Column = i, StartTime = Time.Current + 2000 };
|
||||
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
stage.Add(new DrawableNote(obj, stage.Columns[i].Action));
|
||||
stage.Add(new DrawableNote(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
var obj = new HoldNote { Column = i, StartTime = Time.Current + 2000, Duration = 500 };
|
||||
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
stage.Add(new DrawableHoldNote(obj, stage.Columns[i].Action));
|
||||
stage.Add(new DrawableHoldNote(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mania.Judgements
|
||||
public class HoldNoteJudgement : ManiaJudgement
|
||||
{
|
||||
public override bool AffectsCombo => false;
|
||||
|
||||
protected override int NumericResultFor(HitResult result) => 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
private readonly Container<DrawableHoldNoteTick> tickContainer;
|
||||
|
||||
public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
|
||||
: base(hitObject, action)
|
||||
public DrawableHoldNote(HoldNote hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
@@ -57,12 +57,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
HoldStartTime = () => holdStartTime
|
||||
})
|
||||
},
|
||||
head = new DrawableHeadNote(this, action)
|
||||
head = new DrawableHeadNote(this)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
},
|
||||
tail = new DrawableTailNote(this, action)
|
||||
tail = new DrawableTailNote(this)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
@@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime)
|
||||
return false;
|
||||
|
||||
if (action != Action)
|
||||
if (action != Action.Value)
|
||||
return false;
|
||||
|
||||
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
|
||||
@@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (!holdStartTime.HasValue)
|
||||
return false;
|
||||
|
||||
if (action != Action)
|
||||
if (action != Action.Value)
|
||||
return false;
|
||||
|
||||
holdStartTime = null;
|
||||
@@ -154,8 +154,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
private readonly DrawableHoldNote holdNote;
|
||||
|
||||
public DrawableHeadNote(DrawableHoldNote holdNote, ManiaAction action)
|
||||
: base(holdNote.HitObject.Head, action)
|
||||
public DrawableHeadNote(DrawableHoldNote holdNote)
|
||||
: base(holdNote.HitObject.Head)
|
||||
{
|
||||
this.holdNote = holdNote;
|
||||
}
|
||||
@@ -191,8 +191,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
private readonly DrawableHoldNote holdNote;
|
||||
|
||||
public DrawableTailNote(DrawableHoldNote holdNote, ManiaAction action)
|
||||
: base(holdNote.HitObject.Tail, action)
|
||||
public DrawableTailNote(DrawableHoldNote holdNote)
|
||||
: base(holdNote.HitObject.Tail)
|
||||
{
|
||||
this.holdNote = holdNote;
|
||||
}
|
||||
@@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (!holdNote.holdStartTime.HasValue)
|
||||
return false;
|
||||
|
||||
if (action != Action)
|
||||
if (action != Action.Value)
|
||||
return false;
|
||||
|
||||
UpdateJudgement(true);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -10,30 +11,26 @@ using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableManiaHitObject<TObject> : DrawableHitObject<ManiaHitObject>
|
||||
where TObject : ManiaHitObject
|
||||
public abstract class DrawableManiaHitObject : DrawableHitObject<ManiaHitObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// The key that will trigger input for this hit object.
|
||||
/// The <see cref="ManiaAction"/> which causes this <see cref="DrawableManiaHitObject{TObject}"/> to be hit.
|
||||
/// </summary>
|
||||
protected ManiaAction Action { get; }
|
||||
|
||||
public new TObject HitObject;
|
||||
protected readonly IBindable<ManiaAction> Action = new Bindable<ManiaAction>();
|
||||
|
||||
protected readonly IBindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
|
||||
protected DrawableManiaHitObject(ManiaHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
|
||||
if (action != null)
|
||||
Action = action.Value;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([CanBeNull] IBindable<ManiaAction> action, [NotNull] IScrollingInfo scrollingInfo)
|
||||
{
|
||||
if (action != null)
|
||||
Action.BindTo(action);
|
||||
|
||||
Direction.BindTo(scrollingInfo.Direction);
|
||||
Direction.BindValueChanged(OnDirectionChanged, true);
|
||||
}
|
||||
@@ -42,6 +39,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
Anchor = Origin = direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
|
||||
where TObject : ManiaHitObject
|
||||
{
|
||||
public new readonly TObject HitObject;
|
||||
|
||||
protected DrawableManiaHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
|
||||
@@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
private readonly NotePiece headPiece;
|
||||
|
||||
public DrawableNote(Note hitObject, ManiaAction action)
|
||||
: base(hitObject, action)
|
||||
public DrawableNote(Note hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
@@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
public virtual bool OnPressed(ManiaAction action)
|
||||
{
|
||||
if (action != Action)
|
||||
if (action != Action.Value)
|
||||
return false;
|
||||
|
||||
return UpdateJudgement(true);
|
||||
|
||||
@@ -7,6 +7,8 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.UI.Components;
|
||||
@@ -19,21 +21,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private const float column_width = 45;
|
||||
private const float special_column_width = 70;
|
||||
|
||||
private ManiaAction action;
|
||||
|
||||
public ManiaAction Action
|
||||
{
|
||||
get => action;
|
||||
set
|
||||
{
|
||||
if (action == value)
|
||||
return;
|
||||
action = value;
|
||||
|
||||
background.Action = value;
|
||||
keyArea.Action = value;
|
||||
}
|
||||
}
|
||||
public readonly Bindable<ManiaAction> Action = new Bindable<ManiaAction>();
|
||||
|
||||
private readonly ColumnBackground background;
|
||||
private readonly ColumnKeyArea keyArea;
|
||||
@@ -130,6 +118,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
dependencies.CacheAs<IBindable<ManiaAction>>(Action);
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a DrawableHitObject to this Playfield.
|
||||
/// </summary>
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
{
|
||||
public class ColumnBackground : CompositeDrawable, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
||||
{
|
||||
public ManiaAction Action;
|
||||
private readonly IBindable<ManiaAction> action = new Bindable<ManiaAction>();
|
||||
|
||||
private Box background;
|
||||
private Box backgroundOverlay;
|
||||
@@ -25,8 +25,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
private void load(IBindable<ManiaAction> action, IScrollingInfo scrollingInfo)
|
||||
{
|
||||
this.action.BindTo(action);
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
background = new Box
|
||||
@@ -91,14 +93,14 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
{
|
||||
if (action == Action)
|
||||
if (action == this.action.Value)
|
||||
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(ManiaAction action)
|
||||
{
|
||||
if (action == Action)
|
||||
if (action == this.action.Value)
|
||||
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,15 +21,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
private const float key_icon_size = 10;
|
||||
private const float key_icon_corner_radius = 3;
|
||||
|
||||
public ManiaAction Action;
|
||||
|
||||
private readonly IBindable<ManiaAction> action = new Bindable<ManiaAction>();
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
private Container keyIcon;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
private void load(IBindable<ManiaAction> action, IScrollingInfo scrollingInfo)
|
||||
{
|
||||
this.action.BindTo(action);
|
||||
|
||||
Drawable gradient;
|
||||
|
||||
InternalChildren = new[]
|
||||
@@ -107,14 +108,14 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
{
|
||||
if (action == Action)
|
||||
if (action == this.action.Value)
|
||||
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint).Then().ScaleTo(1.3f, 250, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(ManiaAction action)
|
||||
{
|
||||
if (action == Action)
|
||||
if (action == this.action.Value)
|
||||
keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -101,17 +101,15 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
protected override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h)
|
||||
{
|
||||
ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action;
|
||||
|
||||
var holdNote = h as HoldNote;
|
||||
if (holdNote != null)
|
||||
return new DrawableHoldNote(holdNote, action);
|
||||
|
||||
var note = h as Note;
|
||||
if (note != null)
|
||||
return new DrawableNote(note, action);
|
||||
|
||||
return null;
|
||||
switch (h)
|
||||
{
|
||||
case HoldNote holdNote:
|
||||
return new DrawableHoldNote(holdNote);
|
||||
case Note note:
|
||||
return new DrawableNote(note);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
var column = new Column(direction)
|
||||
{
|
||||
IsSpecial = isSpecial,
|
||||
Action = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++
|
||||
Action = { Value = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ }
|
||||
};
|
||||
|
||||
AddColumn(column);
|
||||
|
||||
@@ -8,17 +8,19 @@ using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
internal class OsuBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
[TestFixture]
|
||||
public class OsuBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||
|
||||
[TestCase("basic")]
|
||||
[TestCase("colinear-perfect-curve")]
|
||||
[TestCase("slider-ticks")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
@@ -26,24 +28,30 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
var startPosition = (hitObject as IHasPosition)?.Position ?? new Vector2(256, 192);
|
||||
var endPosition = (hitObject as Slider)?.EndPosition ?? startPosition;
|
||||
|
||||
yield return new ConvertValue
|
||||
switch (hitObject)
|
||||
{
|
||||
StartTime = hitObject.StartTime,
|
||||
EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime,
|
||||
StartX = startPosition.X,
|
||||
StartY = startPosition.Y,
|
||||
EndX = endPosition.X,
|
||||
EndY = endPosition.Y
|
||||
case Slider slider:
|
||||
foreach (var nested in slider.NestedHitObjects)
|
||||
yield return createConvertValue(nested);
|
||||
break;
|
||||
default:
|
||||
yield return createConvertValue(hitObject);
|
||||
break;
|
||||
}
|
||||
|
||||
ConvertValue createConvertValue(HitObject obj) => new ConvertValue
|
||||
{
|
||||
StartTime = obj.StartTime,
|
||||
EndTime = (obj as IHasEndTime)?.EndTime ?? obj.StartTime,
|
||||
X = (obj as IHasPosition)?.X ?? OsuPlayfield.BASE_SIZE.X / 2,
|
||||
Y = (obj as IHasPosition)?.Y ?? OsuPlayfield.BASE_SIZE.Y / 2,
|
||||
};
|
||||
}
|
||||
|
||||
protected override Ruleset CreateRuleset() => new OsuRuleset();
|
||||
}
|
||||
|
||||
internal struct ConvertValue : IEquatable<ConvertValue>
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// A sane value to account for osu!stable using ints everwhere.
|
||||
@@ -52,17 +60,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
public double StartTime;
|
||||
public double EndTime;
|
||||
public float StartX;
|
||||
public float StartY;
|
||||
public float EndX;
|
||||
public float EndY;
|
||||
public float X;
|
||||
public float Y;
|
||||
|
||||
public bool Equals(ConvertValue other)
|
||||
=> Precision.AlmostEquals(StartTime, other.StartTime)
|
||||
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
||||
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
|
||||
&& Precision.AlmostEquals(StartX, other.StartX)
|
||||
&& Precision.AlmostEquals(StartY, other.StartY, conversion_lenience)
|
||||
&& Precision.AlmostEquals(EndX, other.EndX, conversion_lenience)
|
||||
&& Precision.AlmostEquals(EndY, other.EndY, conversion_lenience);
|
||||
&& Precision.AlmostEquals(X, other.X, conversion_lenience)
|
||||
&& Precision.AlmostEquals(Y, other.Y, conversion_lenience);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
var endTimeData = original as IHasEndTime;
|
||||
var positionData = original as IHasPosition;
|
||||
var comboData = original as IHasCombo;
|
||||
var legacyOffset = original as IHasLegacyLastTickOffset;
|
||||
|
||||
if (curveData != null)
|
||||
{
|
||||
@@ -40,7 +41,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
RepeatSamples = curveData.RepeatSamples,
|
||||
RepeatCount = curveData.RepeatCount,
|
||||
Position = positionData?.Position ?? Vector2.Zero,
|
||||
NewCombo = comboData?.NewCombo ?? false
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset
|
||||
};
|
||||
}
|
||||
else if (endTimeData != null)
|
||||
|
||||
@@ -15,10 +15,10 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
{
|
||||
}
|
||||
|
||||
public override void PostProcess()
|
||||
public override void PreProcess()
|
||||
{
|
||||
base.PreProcess();
|
||||
applyStacking((Beatmap<OsuHitObject>)Beatmap);
|
||||
base.PostProcess();
|
||||
}
|
||||
|
||||
private void applyStacking(Beatmap<OsuHitObject> beatmap)
|
||||
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
continue;
|
||||
|
||||
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
|
||||
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency;
|
||||
|
||||
if (objectN.StartTime - endTime > stackThreshold)
|
||||
//We are no longer within stacking range of the next object.
|
||||
@@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
OsuHitObject objectI = beatmap.HitObjects[i];
|
||||
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
||||
|
||||
double stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
double stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo.StackLeniency;
|
||||
|
||||
/* If this object is a hitcircle, then we enter this "special" case.
|
||||
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@@ -12,11 +11,6 @@ namespace osu.Game.Rulesets.Osu.Judgements
|
||||
{
|
||||
public override HitResult MaxResult => HitResult.Great;
|
||||
|
||||
/// <summary>
|
||||
/// The positional hit offset.
|
||||
/// </summary>
|
||||
public Vector2 PositionOffset;
|
||||
|
||||
protected override int NumericResultFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Osu.Judgements
|
||||
public class OsuSliderTailJudgement : OsuJudgement
|
||||
{
|
||||
public override bool AffectsCombo => false;
|
||||
|
||||
protected override int NumericResultFor(HitResult result) => 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
AddJudgement(new OsuJudgement
|
||||
{
|
||||
Result = result,
|
||||
PositionOffset = Vector2.Zero //todo: set to correct value
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
base.AccentColour = value;
|
||||
Body.AccentColour = AccentColour;
|
||||
Ball.AccentColour = AccentColour;
|
||||
if (HasNestedHitObjects)
|
||||
foreach (var drawableHitObject in NestedHitObjects)
|
||||
drawableHitObject.AccentColour = AccentColour;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
public virtual bool NewCombo { get; set; }
|
||||
|
||||
public int IndexInCurrentCombo { get; set; }
|
||||
public virtual int IndexInCurrentCombo { get; set; }
|
||||
|
||||
public int ComboIndex { get; set; }
|
||||
public virtual int ComboIndex { get; set; }
|
||||
|
||||
public bool LastInCombo { get; set; }
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System.Collections.Generic;
|
||||
@@ -25,6 +26,28 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
|
||||
public override Vector2 EndPosition => Position + this.CurvePositionAt(1);
|
||||
|
||||
public override int ComboIndex
|
||||
{
|
||||
get => base.ComboIndex;
|
||||
set
|
||||
{
|
||||
base.ComboIndex = value;
|
||||
foreach (var n in NestedHitObjects.OfType<IHasComboInformation>())
|
||||
n.ComboIndex = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override int IndexInCurrentCombo
|
||||
{
|
||||
get => base.IndexInCurrentCombo;
|
||||
set
|
||||
{
|
||||
base.IndexInCurrentCombo = value;
|
||||
foreach (var n in NestedHitObjects.OfType<IHasComboInformation>())
|
||||
n.IndexInCurrentCombo = value;
|
||||
}
|
||||
}
|
||||
|
||||
public SliderCurve Curve { get; } = new SliderCurve();
|
||||
|
||||
public List<Vector2> ControlPoints
|
||||
@@ -45,6 +68,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
set { Curve.Distance = value; }
|
||||
}
|
||||
|
||||
public double? LegacyLastTickOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The position of the cursor at the point of completion of this <see cref="Slider"/> if it was hit
|
||||
/// with as few movements as possible. This is set and used by difficulty calculation.
|
||||
@@ -91,6 +116,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
createSliderEnds();
|
||||
createTicks();
|
||||
createRepeatPoints();
|
||||
|
||||
if (LegacyLastTickOffset != null)
|
||||
TailCircle.StartTime = Math.Max(StartTime + Duration / 2, TailCircle.StartTime - LegacyLastTickOffset.Value);
|
||||
}
|
||||
|
||||
private void createSliderEnds()
|
||||
@@ -141,7 +169,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
var distanceProgress = d / length;
|
||||
var timeProgress = reversed ? 1 - distanceProgress : distanceProgress;
|
||||
|
||||
var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL) ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
|
||||
var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL)
|
||||
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
|
||||
var sampleList = new List<SampleInfo>();
|
||||
|
||||
if (firstSample != null)
|
||||
|
||||
@@ -6,6 +6,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Users;
|
||||
|
||||
@@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
/// <summary>
|
||||
/// Constants (for spinners).
|
||||
/// </summary>
|
||||
protected static readonly Vector2 SPINNER_CENTRE = new Vector2(256, 192);
|
||||
protected static readonly Vector2 SPINNER_CENTRE = OsuPlayfield.BASE_SIZE / 2;
|
||||
protected const float SPIN_RADIUS = 50;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,124 +1,256 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 500,
|
||||
"Objects": [{
|
||||
"StartTime": 500,
|
||||
"EndTime": 2500,
|
||||
"StartX": 96,
|
||||
"StartY": 192,
|
||||
"EndX": 96,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 3000,
|
||||
"Objects": [{
|
||||
"StartTime": 3000,
|
||||
"EndTime": 4000,
|
||||
"StartX": 256,
|
||||
"StartY": 192,
|
||||
"EndX": 256,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 4500,
|
||||
"Objects": [{
|
||||
"StartTime": 4500,
|
||||
"EndTime": 5500,
|
||||
"StartX": 256,
|
||||
"StartY": 192,
|
||||
"EndX": 256,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 6000,
|
||||
"Objects": [{
|
||||
"StartTime": 6000,
|
||||
"EndTime": 6500,
|
||||
"StartX": 256,
|
||||
"StartY": 192,
|
||||
"EndX": 256,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 7000,
|
||||
"Objects": [{
|
||||
"StartTime": 7000,
|
||||
"EndTime": 8000,
|
||||
"StartX": 256,
|
||||
"StartY": 128,
|
||||
"EndX": 256,
|
||||
"EndY": 128
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 8500,
|
||||
"Objects": [{
|
||||
"StartTime": 8500,
|
||||
"EndTime": 10999,
|
||||
"StartX": 32,
|
||||
"StartY": 192,
|
||||
"EndX": 508.166229,
|
||||
"EndY": 153.299271
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 11500,
|
||||
"Objects": [{
|
||||
"StartTime": 11500,
|
||||
"EndTime": 12000,
|
||||
"StartX": 256,
|
||||
"StartY": 192,
|
||||
"EndX": 256,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 12500,
|
||||
"Objects": [{
|
||||
"StartTime": 12500,
|
||||
"EndTime": 16500,
|
||||
"StartX": 512,
|
||||
"StartY": 320,
|
||||
"EndX": 291.1977,
|
||||
"EndY": 40.799427
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 17000,
|
||||
"Objects": [{
|
||||
"StartTime": 17000,
|
||||
"EndTime": 18000,
|
||||
"StartX": 256,
|
||||
"StartY": 256,
|
||||
"EndX": 256,
|
||||
"EndY": 256
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 18500,
|
||||
"Objects": [{
|
||||
"StartTime": 18500,
|
||||
"EndTime": 19450,
|
||||
"StartX": 256,
|
||||
"StartY": 192,
|
||||
"EndX": 256,
|
||||
"EndY": 192
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 19875,
|
||||
"Objects": [{
|
||||
"StartTime": 19875,
|
||||
"EndTime": 23874,
|
||||
"StartX": 216,
|
||||
"StartY": 231,
|
||||
"EndX": 408.720825,
|
||||
"EndY": 339.810455
|
||||
}]
|
||||
}
|
||||
]
|
||||
"StartTime": 500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 500.0,
|
||||
"EndTime": 500.0,
|
||||
"X": 96.0,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 1000.0,
|
||||
"EndTime": 1000.0,
|
||||
"X": 256.0,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 1500.0,
|
||||
"EndTime": 1500.0,
|
||||
"X": 416.0,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 2000.0,
|
||||
"EndTime": 2000.0,
|
||||
"X": 256.0,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 2464.0,
|
||||
"EndTime": 2464.0,
|
||||
"X": 96.0,
|
||||
"Y": 192.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 3000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 3000.0,
|
||||
"EndTime": 4000.0,
|
||||
"X": 256.0,
|
||||
"Y": 192.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 4500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 4500.0,
|
||||
"EndTime": 5500.0,
|
||||
"X": 256.0,
|
||||
"Y": 192.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 6000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 6000.0,
|
||||
"EndTime": 6500.0,
|
||||
"X": 256.0,
|
||||
"Y": 192.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 7000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 7000.0,
|
||||
"EndTime": 7000.0,
|
||||
"X": 256.0,
|
||||
"Y": 128.0
|
||||
}, {
|
||||
"StartTime": 7250.0,
|
||||
"EndTime": 7250.0,
|
||||
"X": 336.0,
|
||||
"Y": 128.0
|
||||
}, {
|
||||
"StartTime": 7500.0,
|
||||
"EndTime": 7500.0,
|
||||
"X": 256.0,
|
||||
"Y": 128.0
|
||||
}, {
|
||||
"StartTime": 7750.0,
|
||||
"EndTime": 7750.0,
|
||||
"X": 336.0,
|
||||
"Y": 128.0
|
||||
}, {
|
||||
"StartTime": 7964.0,
|
||||
"EndTime": 7964.0,
|
||||
"X": 256.0,
|
||||
"Y": 128.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 8500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 8500.0,
|
||||
"EndTime": 8500.0,
|
||||
"X": 32.0,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 9000.0,
|
||||
"EndTime": 9000.0,
|
||||
"X": 101.81015,
|
||||
"Y": 326.4915
|
||||
}, {
|
||||
"StartTime": 9500.0,
|
||||
"EndTime": 9500.0,
|
||||
"X": 237.2304,
|
||||
"Y": 276.282928
|
||||
}, {
|
||||
"StartTime": 10000.0,
|
||||
"EndTime": 10000.0,
|
||||
"X": 270.339874,
|
||||
"Y": 121.1423
|
||||
}, {
|
||||
"StartTime": 10500.0,
|
||||
"EndTime": 10500.0,
|
||||
"X": 401.0588,
|
||||
"Y": 49.1515045
|
||||
}, {
|
||||
"StartTime": 10964.0,
|
||||
"EndTime": 10964.0,
|
||||
"X": 508.166229,
|
||||
"Y": 153.299271
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 11500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 11500.0,
|
||||
"EndTime": 12000.0,
|
||||
"X": 256.0,
|
||||
"Y": 192.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 12500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 12500.0,
|
||||
"EndTime": 12500.0,
|
||||
"X": 512.0,
|
||||
"Y": 320.0
|
||||
}, {
|
||||
"StartTime": 13000.0,
|
||||
"EndTime": 13000.0,
|
||||
"X": 353.235535,
|
||||
"Y": 300.154449
|
||||
}, {
|
||||
"StartTime": 13500.0,
|
||||
"EndTime": 13500.0,
|
||||
"X": 194.471069,
|
||||
"Y": 280.3089
|
||||
}, {
|
||||
"StartTime": 14000.0,
|
||||
"EndTime": 14000.0,
|
||||
"X": 35.7066345,
|
||||
"Y": 260.463318
|
||||
}, {
|
||||
"StartTime": 14500.0,
|
||||
"EndTime": 14500.0,
|
||||
"X": 118.370323,
|
||||
"Y": 219.009277
|
||||
}, {
|
||||
"StartTime": 15000.0,
|
||||
"EndTime": 15000.0,
|
||||
"X": 271.087128,
|
||||
"Y": 171.285278
|
||||
}, {
|
||||
"StartTime": 15500.0,
|
||||
"EndTime": 15500.0,
|
||||
"X": 423.803925,
|
||||
"Y": 123.561279
|
||||
}, {
|
||||
"StartTime": 16000.0,
|
||||
"EndTime": 16000.0,
|
||||
"X": 446.420532,
|
||||
"Y": 79.60513
|
||||
}, {
|
||||
"StartTime": 16464.0,
|
||||
"EndTime": 16464.0,
|
||||
"X": 291.1977,
|
||||
"Y": 40.799427
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 17000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 17000.0,
|
||||
"EndTime": 17000.0,
|
||||
"X": 256.0,
|
||||
"Y": 256.0
|
||||
}, {
|
||||
"StartTime": 17250.0,
|
||||
"EndTime": 17250.0,
|
||||
"X": 176.0,
|
||||
"Y": 256.0
|
||||
}, {
|
||||
"StartTime": 17500.0,
|
||||
"EndTime": 17500.0,
|
||||
"X": 256.0,
|
||||
"Y": 256.0
|
||||
}, {
|
||||
"StartTime": 17750.0,
|
||||
"EndTime": 17750.0,
|
||||
"X": 176.0,
|
||||
"Y": 256.0
|
||||
}, {
|
||||
"StartTime": 17964.0,
|
||||
"EndTime": 17964.0,
|
||||
"X": 256.0,
|
||||
"Y": 256.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 18500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 18500.0,
|
||||
"EndTime": 19450.0,
|
||||
"X": 256.0,
|
||||
"Y": 192.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 19875.0,
|
||||
"Objects": [{
|
||||
"StartTime": 19875.0,
|
||||
"EndTime": 19875.0,
|
||||
"X": 216.0,
|
||||
"Y": 231.0
|
||||
}, {
|
||||
"StartTime": 20375.0,
|
||||
"EndTime": 20375.0,
|
||||
"X": 317.446747,
|
||||
"Y": 171.345245
|
||||
}, {
|
||||
"StartTime": 20875.0,
|
||||
"EndTime": 20875.0,
|
||||
"X": 270.3294,
|
||||
"Y": 310.4395
|
||||
}, {
|
||||
"StartTime": 21375.0,
|
||||
"EndTime": 21375.0,
|
||||
"X": 119.121056,
|
||||
"Y": 322.8657
|
||||
}, {
|
||||
"StartTime": 21875.0,
|
||||
"EndTime": 21875.0,
|
||||
"X": 124.28746,
|
||||
"Y": 165.224731
|
||||
}, {
|
||||
"StartTime": 22375.0,
|
||||
"EndTime": 22375.0,
|
||||
"X": 240.4715,
|
||||
"Y": 62.65587
|
||||
}, {
|
||||
"StartTime": 22875.0,
|
||||
"EndTime": 22875.0,
|
||||
"X": 398.054047,
|
||||
"Y": 39.064167
|
||||
}, {
|
||||
"StartTime": 23375.0,
|
||||
"EndTime": 23375.0,
|
||||
"X": 439.749878,
|
||||
"Y": 183.668091
|
||||
}, {
|
||||
"StartTime": 23839.0,
|
||||
"EndTime": 23839.0,
|
||||
"X": 408.720825,
|
||||
"Y": 339.810455
|
||||
}]
|
||||
}]
|
||||
}
|
||||
+13
-10
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 118858,
|
||||
"Objects": [{
|
||||
"StartTime": 118858,
|
||||
"EndTime": 119088,
|
||||
"StartX": 219,
|
||||
"StartY": 215,
|
||||
"EndX": 239.6507,
|
||||
"EndY": 29.1437378
|
||||
"Mappings": [{
|
||||
"StartTime": 118858.0,
|
||||
"Objects": [{
|
||||
"StartTime": 118858.0,
|
||||
"EndTime": 118858.0,
|
||||
"X": 219.0,
|
||||
"Y": 215.0
|
||||
}, {
|
||||
"StartTime": 119052.0,
|
||||
"EndTime": 119052.0,
|
||||
"X": 239.6507,
|
||||
"Y": 29.1437378
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 500.0,
|
||||
"EndTime": 500.0,
|
||||
"X": 96.0,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 624.0,
|
||||
"EndTime": 624.0,
|
||||
"X": 105.921242,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 749.0,
|
||||
"EndTime": 749.0,
|
||||
"X": 115.922493,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 874.0,
|
||||
"EndTime": 874.0,
|
||||
"X": 125.923737,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 999.0,
|
||||
"EndTime": 999.0,
|
||||
"X": 135.924988,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 1124.0,
|
||||
"EndTime": 1124.0,
|
||||
"X": 145.926239,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 1249.0,
|
||||
"EndTime": 1249.0,
|
||||
"X": 155.92749,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 1374.0,
|
||||
"EndTime": 1374.0,
|
||||
"X": 165.928741,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 1499.0,
|
||||
"EndTime": 1499.0,
|
||||
"X": 175.93,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 1624.0,
|
||||
"EndTime": 1624.0,
|
||||
"X": 185.931244,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 1749.0,
|
||||
"EndTime": 1749.0,
|
||||
"X": 195.9325,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 1874.0,
|
||||
"EndTime": 1874.0,
|
||||
"X": 205.933746,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 1999.0,
|
||||
"EndTime": 1999.0,
|
||||
"X": 215.935,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 2124.0,
|
||||
"EndTime": 2124.0,
|
||||
"X": 225.936234,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 2249.0,
|
||||
"EndTime": 2249.0,
|
||||
"X": 235.9375,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 2374.0,
|
||||
"EndTime": 2374.0,
|
||||
"X": 245.938751,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 2499.0,
|
||||
"EndTime": 2499.0,
|
||||
"X": 255.94,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 2624.0,
|
||||
"EndTime": 2624.0,
|
||||
"X": 265.941223,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 2749.0,
|
||||
"EndTime": 2749.0,
|
||||
"X": 275.9425,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 2874.0,
|
||||
"EndTime": 2874.0,
|
||||
"X": 285.943756,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 2999.0,
|
||||
"EndTime": 2999.0,
|
||||
"X": 295.945,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 3124.0,
|
||||
"EndTime": 3124.0,
|
||||
"X": 305.946259,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 3249.0,
|
||||
"EndTime": 3249.0,
|
||||
"X": 315.9475,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 3374.0,
|
||||
"EndTime": 3374.0,
|
||||
"X": 325.94873,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 3499.0,
|
||||
"EndTime": 3499.0,
|
||||
"X": 335.949982,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 3624.0,
|
||||
"EndTime": 3624.0,
|
||||
"X": 345.951233,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 3749.0,
|
||||
"EndTime": 3749.0,
|
||||
"X": 355.952484,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 3874.0,
|
||||
"EndTime": 3874.0,
|
||||
"X": 365.953766,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 3999.0,
|
||||
"EndTime": 3999.0,
|
||||
"X": 375.955,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 4124.0,
|
||||
"EndTime": 4124.0,
|
||||
"X": 385.956238,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 4249.0,
|
||||
"EndTime": 4249.0,
|
||||
"X": 395.9575,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 4374.0,
|
||||
"EndTime": 4374.0,
|
||||
"X": 405.95874,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 4499.0,
|
||||
"EndTime": 4499.0,
|
||||
"X": 415.960022,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 4624.0,
|
||||
"EndTime": 4624.0,
|
||||
"X": 406.038757,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 4749.0,
|
||||
"EndTime": 4749.0,
|
||||
"X": 396.0375,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 4874.0,
|
||||
"EndTime": 4874.0,
|
||||
"X": 386.036255,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 4999.0,
|
||||
"EndTime": 4999.0,
|
||||
"X": 376.035034,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 5124.0,
|
||||
"EndTime": 5124.0,
|
||||
"X": 366.033752,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 5249.0,
|
||||
"EndTime": 5249.0,
|
||||
"X": 356.0325,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 5374.0,
|
||||
"EndTime": 5374.0,
|
||||
"X": 346.03125,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 5499.0,
|
||||
"EndTime": 5499.0,
|
||||
"X": 336.030029,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 5624.0,
|
||||
"EndTime": 5624.0,
|
||||
"X": 326.028748,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 5749.0,
|
||||
"EndTime": 5749.0,
|
||||
"X": 316.0275,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 5874.0,
|
||||
"EndTime": 5874.0,
|
||||
"X": 306.026245,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 5999.0,
|
||||
"EndTime": 5999.0,
|
||||
"X": 296.025,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 6124.0,
|
||||
"EndTime": 6124.0,
|
||||
"X": 286.023773,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 6249.0,
|
||||
"EndTime": 6249.0,
|
||||
"X": 276.022522,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 6374.0,
|
||||
"EndTime": 6374.0,
|
||||
"X": 266.02124,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 6499.0,
|
||||
"EndTime": 6499.0,
|
||||
"X": 256.02,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 6624.0,
|
||||
"EndTime": 6624.0,
|
||||
"X": 246.018768,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 6749.0,
|
||||
"EndTime": 6749.0,
|
||||
"X": 236.017517,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 6874.0,
|
||||
"EndTime": 6874.0,
|
||||
"X": 226.016251,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 6999.0,
|
||||
"EndTime": 6999.0,
|
||||
"X": 216.014984,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 7124.0,
|
||||
"EndTime": 7124.0,
|
||||
"X": 206.013733,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 7249.0,
|
||||
"EndTime": 7249.0,
|
||||
"X": 196.012512,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 7374.0,
|
||||
"EndTime": 7374.0,
|
||||
"X": 186.011261,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 7499.0,
|
||||
"EndTime": 7499.0,
|
||||
"X": 176.01,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 7624.0,
|
||||
"EndTime": 7624.0,
|
||||
"X": 166.008728,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 7749.0,
|
||||
"EndTime": 7749.0,
|
||||
"X": 156.0075,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 7874.0,
|
||||
"EndTime": 7874.0,
|
||||
"X": 146.006256,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 7999.0,
|
||||
"EndTime": 7999.0,
|
||||
"X": 136.005,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 8124.0,
|
||||
"EndTime": 8124.0,
|
||||
"X": 126.003738,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 8249.0,
|
||||
"EndTime": 8249.0,
|
||||
"X": 116.002518,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 8374.0,
|
||||
"EndTime": 8374.0,
|
||||
"X": 106.001259,
|
||||
"Y": 192.0
|
||||
}, {
|
||||
"StartTime": 8463.0,
|
||||
"EndTime": 8463.0,
|
||||
"X": 96.0,
|
||||
"Y": 192.0
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.7
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:6
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8.3
|
||||
SliderMultiplier:0.400000005960464
|
||||
SliderTickRate:4
|
||||
|
||||
[TimingPoints]
|
||||
500,500,4,2,1,50,1,0
|
||||
13426,-100,4,3,1,45,0,0
|
||||
14884,-100,4,2,1,50,0,0
|
||||
|
||||
[HitObjects]
|
||||
96,192,500,6,0,L|416:192,2,320.000004768372
|
||||
@@ -11,7 +11,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
@@ -75,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject)
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition + ((OsuJudgement)judgement).PositionOffset
|
||||
Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition
|
||||
};
|
||||
|
||||
judgementLayer.Add(explosion);
|
||||
|
||||
@@ -12,7 +12,8 @@ using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
internal class TaikoBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
[TestFixture]
|
||||
public class TaikoBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
||||
|
||||
@@ -41,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
protected override Ruleset CreateRuleset() => new TaikoRuleset();
|
||||
}
|
||||
|
||||
internal struct ConvertValue : IEquatable<ConvertValue>
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// A sane value to account for osu!stable using ints everwhere.
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
private void loadBarLines()
|
||||
{
|
||||
TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1];
|
||||
double lastHitTime = 1 + (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime;
|
||||
double lastHitTime = 1 + ((lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime);
|
||||
|
||||
var timingPoints = Beatmap.ControlPointInfo.TimingPoints.ToList();
|
||||
|
||||
|
||||
@@ -65,17 +65,19 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
foreach (var rulesetInfo in rulesets.AvailableRulesets)
|
||||
{
|
||||
var ruleset = rulesetInfo.CreateInstance();
|
||||
var instance = rulesetInfo.CreateInstance();
|
||||
var testBeatmap = createTestBeatmap(rulesetInfo);
|
||||
|
||||
beatmaps.Add(testBeatmap);
|
||||
|
||||
AddStep("set ruleset", () => Ruleset.Value = rulesetInfo);
|
||||
|
||||
selectBeatmap(testBeatmap);
|
||||
|
||||
testBeatmapLabels(ruleset);
|
||||
testBeatmapLabels(instance);
|
||||
|
||||
// TODO: adjust cases once more info is shown for other gamemodes
|
||||
switch (ruleset)
|
||||
switch (instance)
|
||||
{
|
||||
case OsuRuleset _:
|
||||
testInfoLabels(5);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -10,9 +9,6 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using OpenTK;
|
||||
@@ -20,10 +16,9 @@ using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseEditorSeekSnapping : EditorClockTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(HitObjectComposer) };
|
||||
|
||||
public TestCaseEditorSeekSnapping()
|
||||
{
|
||||
BeatDivisor.Value = 4;
|
||||
@@ -56,22 +51,13 @@ namespace osu.Game.Tests.Visual
|
||||
Beatmap.Value = new TestWorkingBeatmap(testBeatmap);
|
||||
|
||||
Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock };
|
||||
|
||||
testSeekNoSnapping();
|
||||
testSeekSnappingOnBeat();
|
||||
testSeekSnappingInBetweenBeat();
|
||||
testSeekForwardNoSnapping();
|
||||
testSeekForwardSnappingOnBeat();
|
||||
testSeekForwardSnappingFromInBetweenBeat();
|
||||
testSeekBackwardSnappingOnBeat();
|
||||
testSeekBackwardSnappingFromInBetweenBeat();
|
||||
testSeekingWithFloatingPointBeatLength();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether time is correctly seeked without snapping.
|
||||
/// </summary>
|
||||
private void testSeekNoSnapping()
|
||||
[Test]
|
||||
public void TestSeekNoSnapping()
|
||||
{
|
||||
reset();
|
||||
|
||||
@@ -94,7 +80,8 @@ namespace osu.Game.Tests.Visual
|
||||
/// Tests whether seeking to exact beat times puts us on the beat time.
|
||||
/// These are the white/yellow ticks on the graph.
|
||||
/// </summary>
|
||||
private void testSeekSnappingOnBeat()
|
||||
[Test]
|
||||
public void TestSeekSnappingOnBeat()
|
||||
{
|
||||
reset();
|
||||
|
||||
@@ -117,9 +104,9 @@ namespace osu.Game.Tests.Visual
|
||||
/// <summary>
|
||||
/// Tests whether seeking to somewhere in the middle between beats puts us on the expected beats.
|
||||
/// For example, snapping between a white/yellow beat should put us on either the yellow or white, depending on which one we're closer too.
|
||||
/// If
|
||||
/// </summary>
|
||||
private void testSeekSnappingInBetweenBeat()
|
||||
[Test]
|
||||
public void TestSeekSnappingInBetweenBeat()
|
||||
{
|
||||
reset();
|
||||
|
||||
@@ -140,7 +127,8 @@ namespace osu.Game.Tests.Visual
|
||||
/// <summary>
|
||||
/// Tests that when seeking forward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it).
|
||||
/// </summary>
|
||||
private void testSeekForwardNoSnapping()
|
||||
[Test]
|
||||
public void TestSeekForwardNoSnapping()
|
||||
{
|
||||
reset();
|
||||
|
||||
@@ -159,7 +147,8 @@ namespace osu.Game.Tests.Visual
|
||||
/// <summary>
|
||||
/// Tests that when seeking forward with beat snapping, all beats are snapped to and timing points are never skipped.
|
||||
/// </summary>
|
||||
private void testSeekForwardSnappingOnBeat()
|
||||
[Test]
|
||||
public void TestSeekForwardSnappingOnBeat()
|
||||
{
|
||||
reset();
|
||||
|
||||
@@ -181,7 +170,8 @@ namespace osu.Game.Tests.Visual
|
||||
/// Tests that when seeking forward from in-between two beats, the next beat or timing point is snapped to, and no beats are skipped.
|
||||
/// This will also test being extremely close to the next beat/timing point, to ensure rounding is not an issue.
|
||||
/// </summary>
|
||||
private void testSeekForwardSnappingFromInBetweenBeat()
|
||||
[Test]
|
||||
public void TestSeekForwardSnappingFromInBetweenBeat()
|
||||
{
|
||||
reset();
|
||||
|
||||
@@ -214,21 +204,20 @@ namespace osu.Game.Tests.Visual
|
||||
/// <summary>
|
||||
/// Tests that when seeking backward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it).
|
||||
/// </summary>
|
||||
private void testSeekBackwardNoSnapping()
|
||||
[Test]
|
||||
public void TestSeekBackwardNoSnapping()
|
||||
{
|
||||
reset();
|
||||
|
||||
AddStep("Seek(450)", () => Clock.Seek(450));
|
||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||
AddAssert("Time = 425", () => Clock.CurrentTime == 425);
|
||||
AddAssert("Time = 400", () => Clock.CurrentTime == 400);
|
||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||
AddAssert("Time = 375", () => Clock.CurrentTime == 375);
|
||||
AddAssert("Time = 350", () => Clock.CurrentTime == 350);
|
||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||
AddAssert("Time = 325", () => Clock.CurrentTime == 325);
|
||||
AddAssert("Time = 150", () => Clock.CurrentTime == 150);
|
||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||
AddAssert("Time = 125", () => Clock.CurrentTime == 125);
|
||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||
AddAssert("Time = 25", () => Clock.CurrentTime == 25);
|
||||
AddAssert("Time = 50", () => Clock.CurrentTime == 50);
|
||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||
AddAssert("Time = 0", () => Clock.CurrentTime == 0);
|
||||
}
|
||||
@@ -236,7 +225,8 @@ namespace osu.Game.Tests.Visual
|
||||
/// <summary>
|
||||
/// Tests that when seeking backward with beat snapping, all beats are snapped to and timing points are never skipped.
|
||||
/// </summary>
|
||||
private void testSeekBackwardSnappingOnBeat()
|
||||
[Test]
|
||||
public void TestSeekBackwardSnappingOnBeat()
|
||||
{
|
||||
reset();
|
||||
|
||||
@@ -259,7 +249,8 @@ namespace osu.Game.Tests.Visual
|
||||
/// Tests that when seeking backward from in-between two beats, the previous beat or timing point is snapped to, and no beats are skipped.
|
||||
/// This will also test being extremely close to the previous beat/timing point, to ensure rounding is not an issue.
|
||||
/// </summary>
|
||||
private void testSeekBackwardSnappingFromInBetweenBeat()
|
||||
[Test]
|
||||
public void TestSeekBackwardSnappingFromInBetweenBeat()
|
||||
{
|
||||
reset();
|
||||
|
||||
@@ -280,7 +271,8 @@ namespace osu.Game.Tests.Visual
|
||||
/// <summary>
|
||||
/// Tests that there are no rounding issues when snapping to beats within a timing point with a floating-point beatlength.
|
||||
/// </summary>
|
||||
private void testSeekingWithFloatingPointBeatLength()
|
||||
[Test]
|
||||
public void TestSeekingWithFloatingPointBeatLength()
|
||||
{
|
||||
reset();
|
||||
|
||||
@@ -288,7 +280,7 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
AddStep("Seek(0)", () => Clock.Seek(0));
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
AddStep("SeekForward, Snap", () =>
|
||||
{
|
||||
@@ -298,7 +290,7 @@ namespace osu.Game.Tests.Visual
|
||||
AddAssert("Time > lastTime", () => Clock.CurrentTime > lastTime);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
AddStep("SeekBackward, Snap", () =>
|
||||
{
|
||||
@@ -316,16 +308,6 @@ namespace osu.Game.Tests.Visual
|
||||
AddStep("Reset", () => Clock.Seek(0));
|
||||
}
|
||||
|
||||
private class TestHitObjectComposer : HitObjectComposer
|
||||
{
|
||||
public TestHitObjectComposer(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IReadOnlyList<ICompositionTool> CompositionTools => new ICompositionTool[0];
|
||||
}
|
||||
|
||||
private class TimingPointVisualiser : CompositeDrawable
|
||||
{
|
||||
private readonly double length;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@@ -17,6 +19,12 @@ namespace osu.Game.Tests.Visual
|
||||
[Description("PlaySongSelect leaderboard")]
|
||||
public class TestCaseLeaderboard : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] {
|
||||
typeof(Placeholder),
|
||||
typeof(MessagePlaceholder),
|
||||
typeof(RetrievalFailurePlaceholder),
|
||||
};
|
||||
|
||||
private RulesetStore rulesets;
|
||||
|
||||
private readonly FailableLeaderboard leaderboard;
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseLoadingAnimation : GridTestCase
|
||||
{
|
||||
public TestCaseLoadingAnimation()
|
||||
: base(2, 2)
|
||||
{
|
||||
LoadingAnimation loading;
|
||||
|
||||
Cell(0).AddRange(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
loading = new LoadingAnimation()
|
||||
});
|
||||
|
||||
loading.Show();
|
||||
|
||||
Cell(1).AddRange(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.White,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
loading = new LoadingAnimation()
|
||||
});
|
||||
|
||||
loading.Show();
|
||||
|
||||
Cell(2).AddRange(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Gray,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
loading = new LoadingAnimation()
|
||||
});
|
||||
|
||||
loading.Show();
|
||||
|
||||
Cell(3).AddRange(new Drawable[]
|
||||
{
|
||||
loading = new LoadingAnimation()
|
||||
});
|
||||
|
||||
Scheduler.AddDelayed(() => loading.ToggleVisibility(), 200, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual
|
||||
foreach (var rulesetInfo in rulesets.AvailableRulesets)
|
||||
{
|
||||
Ruleset ruleset = rulesetInfo.CreateInstance();
|
||||
AddStep($"switch to {ruleset.Description}", () => modSelect.Ruleset.Value = rulesetInfo);
|
||||
AddStep($"switch to {ruleset.Description}", () => Ruleset.Value = rulesetInfo);
|
||||
|
||||
switch (ruleset)
|
||||
{
|
||||
|
||||
@@ -66,8 +66,8 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
// General
|
||||
public int AudioLeadIn { get; set; }
|
||||
public bool Countdown { get; set; }
|
||||
public float StackLeniency { get; set; }
|
||||
public bool Countdown { get; set; } = true;
|
||||
public float StackLeniency { get; set; } = 0.7f;
|
||||
public bool SpecialStyle { get; set; }
|
||||
|
||||
public int RulesetID { get; set; }
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace osu.Game.Beatmaps
|
||||
PostNotification?.Invoke(new SimpleNotification
|
||||
{
|
||||
Icon = FontAwesome.fa_superpowers,
|
||||
Text = "You gotta be a supporter to download for now 'yo"
|
||||
Text = "You gotta be an osu!supporter to download for now 'yo"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,11 +69,22 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new TrackVirtual();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile)));
|
||||
protected override Waveform GetWaveform()
|
||||
{
|
||||
try
|
||||
{
|
||||
var trackData = store.GetStream(getPathForFile(Metadata.AudioFile));
|
||||
return trackData == null ? null : new Waveform(trackData);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Storyboard GetStoryboard()
|
||||
{
|
||||
|
||||
@@ -6,23 +6,9 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public interface IBeatmapProcessor
|
||||
{
|
||||
IBeatmap Beatmap { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Post-processes <see cref="Beatmap"/> to add mode-specific components that aren't added during conversion.
|
||||
/// <para>
|
||||
/// An example of such a usage is for combo colours.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
void PostProcess();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a post-converted Beatmap.
|
||||
/// Provides functionality to alter a <see cref="IBeatmap"/> after it has been converted.
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">The type of HitObject contained in the Beatmap.</typeparam>
|
||||
public class BeatmapProcessor : IBeatmapProcessor
|
||||
{
|
||||
public IBeatmap Beatmap { get; }
|
||||
@@ -32,13 +18,7 @@ namespace osu.Game.Beatmaps
|
||||
Beatmap = beatmap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post-processes a Beatmap to add mode-specific components that aren't added during conversion.
|
||||
/// <para>
|
||||
/// An example of such a usage is for combo colours.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public virtual void PostProcess()
|
||||
public virtual void PreProcess()
|
||||
{
|
||||
IHasComboInformation lastObj = null;
|
||||
|
||||
@@ -62,5 +42,9 @@ namespace osu.Game.Beatmaps
|
||||
lastObj = obj;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void PostProcess()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,13 @@ namespace osu.Game.Beatmaps
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int ID { get; set; }
|
||||
|
||||
public int? OnlineBeatmapSetID { get; set; }
|
||||
private int? onlineBeatmapSetID;
|
||||
|
||||
public int? OnlineBeatmapSetID
|
||||
{
|
||||
get { return onlineBeatmapSetID; }
|
||||
set { onlineBeatmapSetID = value > 0 ? value : null; }
|
||||
}
|
||||
|
||||
public BeatmapMetadata Metadata { get; set; }
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
protected override Texture GetBackground() => game?.Textures.Get(@"Backgrounds/bg4");
|
||||
|
||||
protected override Track GetTrack() => new TrackVirtual();
|
||||
protected override Track GetTrack() => new TrackVirtual { Length = 1000 };
|
||||
|
||||
private class DummyRulesetInfo : RulesetInfo
|
||||
{
|
||||
|
||||
@@ -7,6 +7,9 @@ using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality to convert a <see cref="IBeatmap"/> for a <see cref="Ruleset"/>.
|
||||
/// </summary>
|
||||
public interface IBeatmapConverter
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality to alter a <see cref="IBeatmap"/> after it has been converted.
|
||||
/// </summary>
|
||||
public interface IBeatmapProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IBeatmap"/> to process. This should already be converted to the applicable <see cref="Ruleset"/>.
|
||||
/// </summary>
|
||||
IBeatmap Beatmap { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Processes the converted <see cref="Beatmap"/> prior to <see cref="HitObject.ApplyDefaults"/> being invoked.
|
||||
/// <para>
|
||||
/// Nested <see cref="HitObject"/>s generated during <see cref="HitObject.ApplyDefaults"/> will not be present by this point,
|
||||
/// and no mods will have been applied to the <see cref="HitObject"/>s.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can only be used to add alterations to <see cref="HitObject"/>s generated directly through the conversion process.
|
||||
/// </remarks>
|
||||
void PreProcess();
|
||||
|
||||
/// <summary>
|
||||
/// Processes the converted <see cref="Beatmap"/> after <see cref="HitObject.ApplyDefaults"/> has been invoked.
|
||||
/// <para>
|
||||
/// Nested <see cref="HitObject"/>s generated during <see cref="HitObject.ApplyDefaults"/> will be present by this point,
|
||||
/// and mods will have been applied to all <see cref="HitObject"/>s.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should be used to add alterations to <see cref="HitObject"/>s while they are in their most playable state.
|
||||
/// </remarks>
|
||||
void PostProcess();
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public abstract class WorkingBeatmap : IDisposable
|
||||
public abstract partial class WorkingBeatmap : IDisposable
|
||||
{
|
||||
public readonly BeatmapInfo BeatmapInfo;
|
||||
|
||||
@@ -116,6 +116,10 @@ namespace osu.Game.Beatmaps
|
||||
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
|
||||
}
|
||||
|
||||
IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted);
|
||||
|
||||
processor?.PreProcess();
|
||||
|
||||
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
|
||||
foreach (var obj in converted.HitObjects)
|
||||
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
|
||||
@@ -124,8 +128,7 @@ namespace osu.Game.Beatmaps
|
||||
foreach (var obj in converted.HitObjects)
|
||||
mod.ApplyToHitObject(obj);
|
||||
|
||||
// Post-process
|
||||
rulesetInstance.CreateBeatmapProcessor(converted)?.PostProcess();
|
||||
processor?.PostProcess();
|
||||
|
||||
return converted;
|
||||
}
|
||||
@@ -145,7 +148,7 @@ namespace osu.Game.Beatmaps
|
||||
private Track populateTrack()
|
||||
{
|
||||
// we want to ensure that we always have a track, even if it's a fake one.
|
||||
var t = GetTrack() ?? new TrackVirtual();
|
||||
var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap);
|
||||
applyRateAdjustments(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public partial class WorkingBeatmap
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="TrackVirtual"/> which provides a valid length based on the <see cref="HitObject"/>s of an <see cref="IBeatmap"/>.
|
||||
/// </summary>
|
||||
protected class VirtualBeatmapTrack : TrackVirtual
|
||||
{
|
||||
private const double excess_length = 1000;
|
||||
|
||||
public VirtualBeatmapTrack(IBeatmap beatmap)
|
||||
{
|
||||
var lastObject = beatmap.HitObjects.LastOrDefault();
|
||||
|
||||
switch (lastObject)
|
||||
{
|
||||
case null:
|
||||
Length = excess_length;
|
||||
break;
|
||||
case IHasEndTime endTime:
|
||||
Length = endTime.EndTime + excess_length;
|
||||
break;
|
||||
default:
|
||||
Length = lastObject.StartTime + excess_length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,20 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
public class OsuFocusedOverlayContainer : FocusedOverlayContainer, IPreviewTrackOwner
|
||||
public class OsuFocusedOverlayContainer : FocusedOverlayContainer, IPreviewTrackOwner, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
private SampleChannel samplePopIn;
|
||||
private SampleChannel samplePopOut;
|
||||
|
||||
protected virtual bool PlaySamplesOnStateChange => true;
|
||||
|
||||
private PreviewTrackManager previewTrackManager;
|
||||
|
||||
protected readonly Bindable<OverlayActivation> OverlayActivationMode = new Bindable<OverlayActivation>(OverlayActivation.All);
|
||||
@@ -63,18 +67,33 @@ namespace osu.Game.Graphics.Containers
|
||||
return base.OnClick(state);
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (action == GlobalAction.Back)
|
||||
{
|
||||
State = Visibility.Hidden;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
|
||||
private void onStateChanged(Visibility visibility)
|
||||
{
|
||||
switch (visibility)
|
||||
{
|
||||
case Visibility.Visible:
|
||||
if (OverlayActivationMode != OverlayActivation.Disabled)
|
||||
samplePopIn?.Play();
|
||||
{
|
||||
if (PlaySamplesOnStateChange) samplePopIn?.Play();
|
||||
}
|
||||
else
|
||||
State = Visibility.Hidden;
|
||||
break;
|
||||
case Visibility.Hidden:
|
||||
samplePopOut?.Play();
|
||||
if (PlaySamplesOnStateChange) samplePopOut?.Play();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@@ -16,6 +17,8 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
protected override SpriteText CreateSpriteText() => new OsuSpriteText();
|
||||
|
||||
public void AddArbitraryDrawable(Drawable drawable) => AddInternal(drawable);
|
||||
|
||||
public void AddIcon(FontAwesome icon, Action<SpriteText> creationParameters = null) => AddText(((char)icon).ToString(), creationParameters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Configuration;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Graphics.Cursor
|
||||
{
|
||||
@@ -25,9 +25,8 @@ namespace osu.Game.Graphics.Cursor
|
||||
protected override Drawable CreateCursor() => new Cursor();
|
||||
|
||||
private Bindable<bool> cursorRotate;
|
||||
private bool dragging;
|
||||
|
||||
private bool startRotation;
|
||||
private DragRotationState dragRotationState;
|
||||
private Vector2 positionMouseDown;
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager)
|
||||
@@ -40,18 +39,18 @@ namespace osu.Game.Graphics.Cursor
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
{
|
||||
if (cursorRotate && dragging)
|
||||
if (dragRotationState != DragRotationState.NotDragging)
|
||||
{
|
||||
Debug.Assert(state.Mouse.PositionMouseDown != null);
|
||||
|
||||
var position = state.Mouse.Position;
|
||||
var distance = Vector2Extensions.Distance(position, positionMouseDown);
|
||||
// don't start rotating until we're moved a minimum distance away from the mouse down location,
|
||||
// else it can have an annoying effect.
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
startRotation |= Vector2Extensions.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30;
|
||||
|
||||
if (startRotation)
|
||||
if (dragRotationState == DragRotationState.DragStarted && distance > 30)
|
||||
dragRotationState = DragRotationState.Rotating;
|
||||
// don't rotate when distance is zero to avoid NaN
|
||||
if (dragRotationState == DragRotationState.Rotating && distance > 0)
|
||||
{
|
||||
Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown.Value;
|
||||
Vector2 offset = state.Mouse.Position - positionMouseDown;
|
||||
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
|
||||
|
||||
// Always rotate in the direction of least distance
|
||||
@@ -67,12 +66,6 @@ namespace osu.Game.Graphics.Cursor
|
||||
return base.OnMouseMove(state);
|
||||
}
|
||||
|
||||
protected override bool OnDragStart(InputState state)
|
||||
{
|
||||
dragging = true;
|
||||
return base.OnDragStart(state);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
ActiveCursor.Scale = new Vector2(1);
|
||||
@@ -80,6 +73,12 @@ namespace osu.Game.Graphics.Cursor
|
||||
|
||||
((Cursor)ActiveCursor).AdditiveLayer.Alpha = 0;
|
||||
((Cursor)ActiveCursor).AdditiveLayer.FadeInFromZero(800, Easing.OutQuint);
|
||||
|
||||
if (args.Button == MouseButton.Left && cursorRotate)
|
||||
{
|
||||
dragRotationState = DragRotationState.DragStarted;
|
||||
positionMouseDown = state.Mouse.Position;
|
||||
}
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
@@ -87,14 +86,16 @@ namespace osu.Game.Graphics.Cursor
|
||||
{
|
||||
if (!state.Mouse.HasMainButtonPressed)
|
||||
{
|
||||
dragging = false;
|
||||
startRotation = false;
|
||||
|
||||
((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint);
|
||||
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf);
|
||||
ActiveCursor.ScaleTo(1, 500, Easing.OutElastic);
|
||||
}
|
||||
|
||||
if (args.Button == MouseButton.Left)
|
||||
{
|
||||
if (dragRotationState == DragRotationState.Rotating)
|
||||
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf);
|
||||
dragRotationState = DragRotationState.NotDragging;
|
||||
}
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
|
||||
@@ -160,5 +161,12 @@ namespace osu.Game.Graphics.Cursor
|
||||
cursorScale.TriggerChange();
|
||||
}
|
||||
}
|
||||
|
||||
private enum DragRotationState
|
||||
{
|
||||
NotDragging,
|
||||
DragStarted,
|
||||
Rotating,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,14 +12,14 @@ namespace osu.Game.Graphics
|
||||
{
|
||||
public class DrawableDate : OsuSpriteText, IHasTooltip
|
||||
{
|
||||
private readonly DateTimeOffset date;
|
||||
protected readonly DateTimeOffset Date;
|
||||
|
||||
public DrawableDate(DateTimeOffset date)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Font = "Exo2.0-RegularItalic";
|
||||
|
||||
this.date = date.ToLocalTime();
|
||||
Date = date.ToLocalTime();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -38,7 +38,7 @@ namespace osu.Game.Graphics
|
||||
{
|
||||
updateTime();
|
||||
|
||||
var diffToNow = DateTimeOffset.Now.Subtract(date);
|
||||
var diffToNow = DateTimeOffset.Now.Subtract(Date);
|
||||
|
||||
double timeUntilNextUpdate = 1000;
|
||||
if (diffToNow.TotalSeconds > 60)
|
||||
@@ -58,8 +58,10 @@ namespace osu.Game.Graphics
|
||||
|
||||
public override bool HandleMouseInput => true;
|
||||
|
||||
private void updateTime() => Text = date.Humanize();
|
||||
protected virtual string Format() => Date.Humanize();
|
||||
|
||||
public string TooltipText => date.ToString();
|
||||
private void updateTime() => Text = Format();
|
||||
|
||||
public virtual string TooltipText => string.Format($"{Date:MMMM d, yyyy h:mm tt \"UTC\"z}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Input;
|
||||
using System;
|
||||
using osu.Game.Input.Bindings;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@@ -19,6 +20,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
public Action Exit;
|
||||
|
||||
private bool focus;
|
||||
|
||||
public bool HoldFocus
|
||||
{
|
||||
get { return focus; }
|
||||
@@ -26,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
focus = value;
|
||||
if (!focus && HasFocus)
|
||||
GetContainingInputManager().ChangeFocus(null);
|
||||
base.KillFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,18 +43,34 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (!args.Repeat && args.Key == Key.Escape)
|
||||
{
|
||||
if (Text.Length > 0)
|
||||
Text = string.Empty;
|
||||
else
|
||||
Exit?.Invoke();
|
||||
return true;
|
||||
}
|
||||
if (!HasFocus) return false;
|
||||
|
||||
if (args.Key == Key.Escape)
|
||||
return false; // disable the framework-level handling of escape key for confority (we use GlobalAction.Back).
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
}
|
||||
|
||||
public override bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (action == GlobalAction.Back)
|
||||
{
|
||||
if (Text.Length > 0)
|
||||
{
|
||||
Text = string.Empty;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnPressed(action);
|
||||
}
|
||||
|
||||
protected override void KillFocus()
|
||||
{
|
||||
base.KillFocus();
|
||||
Exit?.Invoke();
|
||||
}
|
||||
|
||||
public override bool RequestsFocus => HoldFocus;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,17 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public class LoadingAnimation : VisibilityContainer
|
||||
{
|
||||
private readonly SpriteIcon spinner;
|
||||
private readonly SpriteIcon spinnerShadow;
|
||||
|
||||
private const float spin_duration = 600;
|
||||
private const float transition_duration = 200;
|
||||
|
||||
public LoadingAnimation()
|
||||
{
|
||||
@@ -20,12 +25,22 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
spinner = new SpriteIcon
|
||||
spinnerShadow = new SpriteIcon
|
||||
{
|
||||
Size = new Vector2(20),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.fa_spinner
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Position = new Vector2(1, 1),
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.4f,
|
||||
Icon = FontAwesome.fa_circle_o_notch
|
||||
},
|
||||
spinner = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Icon = FontAwesome.fa_circle_o_notch
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -34,12 +49,11 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
spinner.Spin(2000, RotationDirection.Clockwise);
|
||||
spinner.Spin(spin_duration, RotationDirection.Clockwise);
|
||||
spinnerShadow.Spin(spin_duration, RotationDirection.Clockwise);
|
||||
}
|
||||
|
||||
private const float transition_duration = 500;
|
||||
|
||||
protected override void PopIn() => this.FadeIn(transition_duration * 5, Easing.OutQuint);
|
||||
protected override void PopIn() => this.FadeIn(transition_duration * 2, Easing.OutQuint);
|
||||
|
||||
protected override void PopOut() => this.FadeOut(transition_duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
protected override Drawable GetDrawableCharacter(char c) => new PasswordMaskChar(CalculatedTextSize);
|
||||
|
||||
public override bool AllowClipboardExport => false;
|
||||
protected override bool AllowClipboardExport => false;
|
||||
|
||||
private readonly CapsWarning warning;
|
||||
|
||||
|
||||
@@ -9,10 +9,12 @@ using osu.Framework.Input;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Input.Bindings;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public class OsuTextBox : TextBox
|
||||
public class OsuTextBox : TextBox, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
protected override Color4 BackgroundUnfocused => Color4.Black.Opacity(0.5f);
|
||||
protected override Color4 BackgroundFocused => OsuColour.Gray(0.3f).Opacity(0.8f);
|
||||
@@ -33,10 +35,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
TextContainer.Height = 0.5f;
|
||||
CornerRadius = 5;
|
||||
|
||||
Current.DisabledChanged += disabled =>
|
||||
{
|
||||
Alpha = disabled ? 0.3f : 1;
|
||||
};
|
||||
Current.DisabledChanged += disabled => { Alpha = disabled ? 0.3f : 1; };
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -59,5 +58,18 @@ namespace osu.Game.Graphics.UserInterface
|
||||
}
|
||||
|
||||
protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), TextSize = CalculatedTextSize };
|
||||
|
||||
public virtual bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (action == GlobalAction.Back)
|
||||
{
|
||||
KillFocus();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (HandlePendingText(state)) return true;
|
||||
|
||||
if (!state.Keyboard.ControlPressed && !state.Keyboard.ShiftPressed)
|
||||
{
|
||||
switch (args.Key)
|
||||
|
||||
@@ -45,7 +45,9 @@ namespace osu.Game.Input.Bindings
|
||||
public IEnumerable<KeyBinding> InGameKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene),
|
||||
new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry)
|
||||
new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed),
|
||||
};
|
||||
|
||||
protected override IEnumerable<Drawable> KeyBindingInputQueue =>
|
||||
@@ -85,6 +87,12 @@ namespace osu.Game.Input.Bindings
|
||||
ToggleGameplayMouseButtons,
|
||||
|
||||
[Description("Go back")]
|
||||
Back
|
||||
Back,
|
||||
|
||||
[Description("Increase scroll speed")]
|
||||
IncreaseScrollSpeed,
|
||||
|
||||
[Description("Decrease scroll speed")]
|
||||
DecreaseScrollSpeed,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Migrations
|
||||
{
|
||||
[DbContext(typeof(OsuDbContext))]
|
||||
[Migration("20180628011956_RemoveNegativeSetIDs")]
|
||||
partial class RemoveNegativeSetIDs
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<float>("ApproachRate");
|
||||
|
||||
b.Property<float>("CircleSize");
|
||||
|
||||
b.Property<float>("DrainRate");
|
||||
|
||||
b.Property<float>("OverallDifficulty");
|
||||
|
||||
b.Property<double>("SliderMultiplier");
|
||||
|
||||
b.Property<double>("SliderTickRate");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.ToTable("BeatmapDifficulty");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AudioLeadIn");
|
||||
|
||||
b.Property<int>("BaseDifficultyID");
|
||||
|
||||
b.Property<int>("BeatDivisor");
|
||||
|
||||
b.Property<int>("BeatmapSetInfoID");
|
||||
|
||||
b.Property<bool>("Countdown");
|
||||
|
||||
b.Property<double>("DistanceSpacing");
|
||||
|
||||
b.Property<int>("GridSize");
|
||||
|
||||
b.Property<string>("Hash");
|
||||
|
||||
b.Property<bool>("Hidden");
|
||||
|
||||
b.Property<bool>("LetterboxInBreaks");
|
||||
|
||||
b.Property<string>("MD5Hash");
|
||||
|
||||
b.Property<int?>("MetadataID");
|
||||
|
||||
b.Property<int?>("OnlineBeatmapID");
|
||||
|
||||
b.Property<string>("Path");
|
||||
|
||||
b.Property<int>("RulesetID");
|
||||
|
||||
b.Property<bool>("SpecialStyle");
|
||||
|
||||
b.Property<float>("StackLeniency");
|
||||
|
||||
b.Property<double>("StarDifficulty");
|
||||
|
||||
b.Property<string>("StoredBookmarks");
|
||||
|
||||
b.Property<double>("TimelineZoom");
|
||||
|
||||
b.Property<string>("Version");
|
||||
|
||||
b.Property<bool>("WidescreenStoryboard");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("BaseDifficultyID");
|
||||
|
||||
b.HasIndex("BeatmapSetInfoID");
|
||||
|
||||
b.HasIndex("Hash");
|
||||
|
||||
b.HasIndex("MD5Hash");
|
||||
|
||||
b.HasIndex("MetadataID");
|
||||
|
||||
b.HasIndex("OnlineBeatmapID")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("RulesetID");
|
||||
|
||||
b.ToTable("BeatmapInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Artist");
|
||||
|
||||
b.Property<string>("ArtistUnicode");
|
||||
|
||||
b.Property<string>("AudioFile");
|
||||
|
||||
b.Property<string>("AuthorString")
|
||||
.HasColumnName("Author");
|
||||
|
||||
b.Property<string>("BackgroundFile");
|
||||
|
||||
b.Property<int>("PreviewTime");
|
||||
|
||||
b.Property<string>("Source");
|
||||
|
||||
b.Property<string>("Tags");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("TitleUnicode");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.ToTable("BeatmapMetadata");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("BeatmapSetInfoID");
|
||||
|
||||
b.Property<int>("FileInfoID");
|
||||
|
||||
b.Property<string>("Filename")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("BeatmapSetInfoID");
|
||||
|
||||
b.HasIndex("FileInfoID");
|
||||
|
||||
b.ToTable("BeatmapSetFileInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("DeletePending");
|
||||
|
||||
b.Property<string>("Hash");
|
||||
|
||||
b.Property<int?>("MetadataID");
|
||||
|
||||
b.Property<int?>("OnlineBeatmapSetID");
|
||||
|
||||
b.Property<bool>("Protected");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("DeletePending");
|
||||
|
||||
b.HasIndex("Hash")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("MetadataID");
|
||||
|
||||
b.HasIndex("OnlineBeatmapSetID")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("BeatmapSetInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("IntKey")
|
||||
.HasColumnName("Key");
|
||||
|
||||
b.Property<int?>("RulesetID");
|
||||
|
||||
b.Property<string>("StringValue")
|
||||
.HasColumnName("Value");
|
||||
|
||||
b.Property<int?>("Variant");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("RulesetID", "Variant");
|
||||
|
||||
b.ToTable("Settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("IntAction")
|
||||
.HasColumnName("Action");
|
||||
|
||||
b.Property<string>("KeysString")
|
||||
.HasColumnName("Keys");
|
||||
|
||||
b.Property<int?>("RulesetID");
|
||||
|
||||
b.Property<int?>("Variant");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("IntAction");
|
||||
|
||||
b.HasIndex("RulesetID", "Variant");
|
||||
|
||||
b.ToTable("KeyBinding");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Hash");
|
||||
|
||||
b.Property<int>("ReferenceCount");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("Hash")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("ReferenceCount");
|
||||
|
||||
b.ToTable("FileInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
|
||||
{
|
||||
b.Property<int?>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<string>("InstantiationInfo");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("ShortName");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("Available");
|
||||
|
||||
b.HasIndex("ShortName")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RulesetInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("FileInfoID");
|
||||
|
||||
b.Property<string>("Filename")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<int>("SkinInfoID");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.HasIndex("FileInfoID");
|
||||
|
||||
b.HasIndex("SkinInfoID");
|
||||
|
||||
b.ToTable("SkinFileInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
|
||||
{
|
||||
b.Property<int>("ID")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Creator");
|
||||
|
||||
b.Property<bool>("DeletePending");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.HasKey("ID");
|
||||
|
||||
b.ToTable("SkinInfo");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
|
||||
.WithMany()
|
||||
.HasForeignKey("BaseDifficultyID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
|
||||
.WithMany("Beatmaps")
|
||||
.HasForeignKey("BeatmapSetInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
|
||||
.WithMany("Beatmaps")
|
||||
.HasForeignKey("MetadataID");
|
||||
|
||||
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
|
||||
.WithMany()
|
||||
.HasForeignKey("RulesetID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
|
||||
.WithMany("Files")
|
||||
.HasForeignKey("BeatmapSetInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
|
||||
.WithMany()
|
||||
.HasForeignKey("FileInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
|
||||
.WithMany("BeatmapSets")
|
||||
.HasForeignKey("MetadataID");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
|
||||
{
|
||||
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
|
||||
.WithMany()
|
||||
.HasForeignKey("FileInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("osu.Game.Skinning.SkinInfo")
|
||||
.WithMany("Files")
|
||||
.HasForeignKey("SkinInfoID")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace osu.Game.Migrations
|
||||
{
|
||||
public partial class RemoveNegativeSetIDs : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// There was a change that baetmaps were being loaded with "-1" online IDs, which is completely incorrect.
|
||||
// This ensures there will not be unique key conflicts as a result of these incorrectly imported beatmaps.
|
||||
migrationBuilder.Sql("UPDATE BeatmapSetInfo SET OnlineBeatmapSetID = null WHERE OnlineBeatmapSetID <= 0");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-5
@@ -85,7 +85,7 @@ namespace osu.Game
|
||||
private OnScreenDisplay onscreenDisplay;
|
||||
|
||||
private Bindable<int> configRuleset;
|
||||
public Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||
|
||||
private Bindable<int> configSkin;
|
||||
|
||||
@@ -147,10 +147,13 @@ namespace osu.Game
|
||||
|
||||
dependencies.CacheAs(this);
|
||||
|
||||
dependencies.CacheAs(ruleset);
|
||||
dependencies.CacheAs<IBindable<RulesetInfo>>(ruleset);
|
||||
|
||||
// bind config int to database RulesetInfo
|
||||
configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset);
|
||||
Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First();
|
||||
Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0;
|
||||
ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First();
|
||||
ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0;
|
||||
|
||||
// bind config int to database SkinInfo
|
||||
configSkin = LocalConfig.GetBindable<int>(OsuSetting.Skin);
|
||||
@@ -216,7 +219,7 @@ namespace osu.Game
|
||||
return;
|
||||
}
|
||||
|
||||
Ruleset.Value = s.Ruleset;
|
||||
ruleset.Value = s.Ruleset;
|
||||
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap);
|
||||
Beatmap.Value.Mods.Value = s.Mods;
|
||||
@@ -550,7 +553,7 @@ namespace osu.Game
|
||||
// the use case for not applying is in visual/unit tests.
|
||||
bool applyRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false;
|
||||
|
||||
Ruleset.Disabled = applyRestrictions;
|
||||
ruleset.Disabled = applyRestrictions;
|
||||
Beatmap.Disabled = applyRestrictions;
|
||||
|
||||
mainContent.Padding = new MarginPadding { Top = ToolbarOffset };
|
||||
|
||||
@@ -6,16 +6,16 @@ using System.Linq;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Overlays.Dialog
|
||||
{
|
||||
@@ -23,6 +23,9 @@ namespace osu.Game.Overlays.Dialog
|
||||
{
|
||||
public static readonly float ENTER_DURATION = 500;
|
||||
public static readonly float EXIT_DURATION = 200;
|
||||
|
||||
protected override bool BlockPassThroughMouse => false;
|
||||
|
||||
private readonly Vector2 ringSize = new Vector2(100f);
|
||||
private readonly Vector2 ringMinifiedSize = new Vector2(20f);
|
||||
private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f);
|
||||
@@ -34,26 +37,28 @@ namespace osu.Game.Overlays.Dialog
|
||||
private readonly SpriteText header;
|
||||
private readonly TextFlowContainer body;
|
||||
|
||||
private bool actionInvoked;
|
||||
|
||||
public FontAwesome Icon
|
||||
{
|
||||
get { return icon.Icon; }
|
||||
set { icon.Icon = value; }
|
||||
get => icon.Icon;
|
||||
set => icon.Icon = value;
|
||||
}
|
||||
|
||||
public string HeaderText
|
||||
{
|
||||
get { return header.Text; }
|
||||
set { header.Text = value; }
|
||||
get => header.Text;
|
||||
set => header.Text = value;
|
||||
}
|
||||
|
||||
public string BodyText
|
||||
{
|
||||
set { body.Text = value; }
|
||||
set => body.Text = value;
|
||||
}
|
||||
|
||||
public IEnumerable<PopupDialogButton> Buttons
|
||||
{
|
||||
get { return buttonsContainer.Children; }
|
||||
get => buttonsContainer.Children;
|
||||
set
|
||||
{
|
||||
buttonsContainer.ChildrenEnumerable = value;
|
||||
@@ -62,71 +67,17 @@ namespace osu.Game.Overlays.Dialog
|
||||
var action = b.Action;
|
||||
b.Action = () =>
|
||||
{
|
||||
if (actionInvoked) return;
|
||||
|
||||
Hide();
|
||||
|
||||
actionInvoked = true;
|
||||
action?.Invoke();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pressButtonAtIndex(int index)
|
||||
{
|
||||
if (index < Buttons.Count())
|
||||
Buttons.Skip(index).First().TriggerOnClick();
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (args.Repeat) return false;
|
||||
|
||||
if (args.Key == Key.Enter || args.Key == Key.KeypadEnter)
|
||||
{
|
||||
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
// press button at number if 1-9 on number row or keypad are pressed
|
||||
var k = args.Key;
|
||||
if (k >= Key.Number1 && k <= Key.Number9)
|
||||
{
|
||||
pressButtonAtIndex(k - Key.Number1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (k >= Key.Keypad1 && k <= Key.Keypad9)
|
||||
{
|
||||
pressButtonAtIndex(k - Key.Keypad1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
|
||||
// Reset various animations but only if the dialog animation fully completed
|
||||
if (content.Alpha == 0)
|
||||
{
|
||||
buttonsContainer.TransformSpacingTo(buttonsEnterSpacing);
|
||||
buttonsContainer.MoveToY(buttonsEnterSpacing.Y);
|
||||
ring.ResizeTo(ringMinifiedSize);
|
||||
}
|
||||
|
||||
content.FadeIn(ENTER_DURATION, Easing.OutQuint);
|
||||
ring.ResizeTo(ringSize, ENTER_DURATION, Easing.OutQuint);
|
||||
buttonsContainer.TransformSpacingTo(Vector2.Zero, ENTER_DURATION, Easing.OutQuint);
|
||||
buttonsContainer.MoveToY(0, ENTER_DURATION, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
|
||||
content.FadeOut(EXIT_DURATION, Easing.InSine);
|
||||
}
|
||||
|
||||
public PopupDialog()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
@@ -136,9 +87,6 @@ namespace osu.Game.Overlays.Dialog
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Width = 0.4f,
|
||||
Alpha = 0f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@@ -243,5 +191,69 @@ namespace osu.Game.Overlays.Dialog
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (args.Repeat) return false;
|
||||
|
||||
if (args.Key == Key.Enter || args.Key == Key.KeypadEnter)
|
||||
{
|
||||
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
// press button at number if 1-9 on number row or keypad are pressed
|
||||
var k = args.Key;
|
||||
if (k >= Key.Number1 && k <= Key.Number9)
|
||||
{
|
||||
pressButtonAtIndex(k - Key.Number1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (k >= Key.Keypad1 && k <= Key.Keypad9)
|
||||
{
|
||||
pressButtonAtIndex(k - Key.Keypad1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
|
||||
actionInvoked = false;
|
||||
|
||||
// Reset various animations but only if the dialog animation fully completed
|
||||
if (content.Alpha == 0)
|
||||
{
|
||||
buttonsContainer.TransformSpacingTo(buttonsEnterSpacing);
|
||||
buttonsContainer.MoveToY(buttonsEnterSpacing.Y);
|
||||
ring.ResizeTo(ringMinifiedSize);
|
||||
}
|
||||
|
||||
content.FadeIn(ENTER_DURATION, Easing.OutQuint);
|
||||
ring.ResizeTo(ringSize, ENTER_DURATION, Easing.OutQuint);
|
||||
buttonsContainer.TransformSpacingTo(Vector2.Zero, ENTER_DURATION, Easing.OutQuint);
|
||||
buttonsContainer.MoveToY(0, ENTER_DURATION, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
if (!actionInvoked)
|
||||
// In the case a user did not choose an action before a hide was triggered, press the last button.
|
||||
// This is presumed to always be a sane default "cancel" action.
|
||||
buttonsContainer.Last().TriggerOnClick();
|
||||
|
||||
base.PopOut();
|
||||
content.FadeOut(EXIT_DURATION, Easing.InSine);
|
||||
}
|
||||
|
||||
private void pressButtonAtIndex(int index)
|
||||
{
|
||||
if (index < Buttons.Count())
|
||||
Buttons.Skip(index).First().TriggerOnClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
@@ -16,6 +13,20 @@ namespace osu.Game.Overlays
|
||||
private readonly Container dialogContainer;
|
||||
private PopupDialog currentDialog;
|
||||
|
||||
public DialogOverlay()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Child = dialogContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
|
||||
Width = 0.4f;
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
}
|
||||
|
||||
public void Push(PopupDialog dialog)
|
||||
{
|
||||
if (dialog == currentDialog) return;
|
||||
@@ -30,6 +41,10 @@ namespace osu.Game.Overlays
|
||||
State = Visibility.Visible;
|
||||
}
|
||||
|
||||
protected override bool PlaySamplesOnStateChange => false;
|
||||
|
||||
protected override bool BlockPassThroughKeyboard => true;
|
||||
|
||||
private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v)
|
||||
{
|
||||
if (v != Visibility.Hidden) return;
|
||||
@@ -50,32 +65,14 @@ namespace osu.Game.Overlays
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine);
|
||||
}
|
||||
|
||||
public DialogOverlay()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
if (currentDialog?.State == Visibility.Visible)
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
},
|
||||
},
|
||||
},
|
||||
dialogContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
currentDialog.Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,15 +35,13 @@ namespace osu.Game.Overlays.Direct
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuGame game, RulesetStore rulesets, OsuColour colours)
|
||||
private void load(RulesetStore rulesets, OsuColour colours, Bindable<RulesetInfo> ruleset)
|
||||
{
|
||||
DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark;
|
||||
|
||||
Ruleset.Value = game?.Ruleset.Value ?? rulesets.GetRuleset(0);
|
||||
Ruleset.Value = ruleset ?? rulesets.GetRuleset(0);
|
||||
foreach (var r in rulesets.AvailableRulesets)
|
||||
{
|
||||
modeButtons.Add(new RulesetToggleButton(Ruleset, r));
|
||||
}
|
||||
}
|
||||
|
||||
private class RulesetToggleButton : OsuClickableContainer
|
||||
|
||||
@@ -10,6 +10,7 @@ using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
@@ -48,9 +49,15 @@ namespace osu.Game.Overlays.Direct
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
icon.FadeTo(0.5f, transition_duration, Easing.OutQuint);
|
||||
loadingAnimation.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
icon.FadeTo(1, transition_duration, Easing.OutQuint);
|
||||
loadingAnimation.Hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +74,10 @@ namespace osu.Game.Overlays.Direct
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Icon = FontAwesome.fa_play,
|
||||
},
|
||||
loadingAnimation = new LoadingAnimation(),
|
||||
loadingAnimation = new LoadingAnimation
|
||||
{
|
||||
Size = new Vector2(15),
|
||||
},
|
||||
});
|
||||
|
||||
Playing.ValueChanged += playingStateChanged;
|
||||
|
||||
@@ -202,9 +202,6 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Escape:
|
||||
finalise();
|
||||
return true;
|
||||
case Key.Delete:
|
||||
{
|
||||
if (state.Keyboard.ShiftPressed)
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>();
|
||||
|
||||
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||
public readonly IBindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||
|
||||
private void rulesetChanged(RulesetInfo newRuleset)
|
||||
{
|
||||
@@ -51,8 +51,8 @@ namespace osu.Game.Overlays.Mods
|
||||
refreshSelectedMods();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets, AudioManager audio)
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, IBindable<RulesetInfo> ruleset, AudioManager audio)
|
||||
{
|
||||
SelectedMods.ValueChanged += selectedModsChanged;
|
||||
|
||||
@@ -60,13 +60,8 @@ namespace osu.Game.Overlays.Mods
|
||||
HighMultiplierColour = colours.Green;
|
||||
UnrankedLabel.Colour = colours.Blue;
|
||||
|
||||
if (osu != null)
|
||||
Ruleset.BindTo(osu.Ruleset);
|
||||
else
|
||||
Ruleset.Value = rulesets.AvailableRulesets.First();
|
||||
|
||||
Ruleset.ValueChanged += rulesetChanged;
|
||||
Ruleset.TriggerChange();
|
||||
Ruleset.BindTo(ruleset);
|
||||
Ruleset.BindValueChanged(rulesetChanged, true);
|
||||
|
||||
sampleOn = audio.Sample.Get(@"UI/check-on");
|
||||
sampleOff = audio.Sample.Get(@"UI/check-off");
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Components
|
||||
{
|
||||
public class DrawableJoinDate : DrawableDate
|
||||
{
|
||||
public DrawableJoinDate(DateTimeOffset date)
|
||||
: base(date)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string Format() => Text = Date.ToUniversalTime().Year < 2008 ? "Here since the beginning" : $"{Date:MMMM yyyy}";
|
||||
|
||||
public override string TooltipText => $"{Date:MMMM d, yyyy}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Components
|
||||
{
|
||||
public class GradeBadge : Container
|
||||
{
|
||||
private const float width = 50;
|
||||
private readonly string grade;
|
||||
private readonly Sprite badge;
|
||||
private readonly SpriteText numberText;
|
||||
|
||||
public int DisplayCount
|
||||
{
|
||||
set => numberText.Text = value.ToString(@"#,0");
|
||||
}
|
||||
|
||||
public GradeBadge(string grade)
|
||||
{
|
||||
this.grade = grade;
|
||||
Width = width;
|
||||
Height = 41;
|
||||
Add(badge = new Sprite
|
||||
{
|
||||
Width = width,
|
||||
Height = 26
|
||||
});
|
||||
Add(numberText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-Bold"
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
badge.Texture = textures.Get($"Grades/{grade}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Profile.Components;
|
||||
using osu.Game.Overlays.Profile.Header;
|
||||
using osu.Game.Users;
|
||||
|
||||
@@ -375,12 +376,12 @@ namespace osu.Game.Overlays.Profile
|
||||
|
||||
if (user.JoinDate.ToUniversalTime().Year < 2008)
|
||||
{
|
||||
infoTextLeft.AddText("Here since the beginning", boldItalic);
|
||||
infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), lightText);
|
||||
}
|
||||
else
|
||||
{
|
||||
infoTextLeft.AddText("Joined ", lightText);
|
||||
infoTextLeft.AddText(new DrawableDate(user.JoinDate), boldItalic);
|
||||
infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), boldItalic);
|
||||
}
|
||||
|
||||
infoTextLeft.NewLine();
|
||||
@@ -470,43 +471,5 @@ namespace osu.Game.Overlays.Profile
|
||||
|
||||
infoTextRight.NewLine();
|
||||
}
|
||||
|
||||
private class GradeBadge : Container
|
||||
{
|
||||
private const float width = 50;
|
||||
private readonly string grade;
|
||||
private readonly Sprite badge;
|
||||
private readonly SpriteText numberText;
|
||||
|
||||
public int DisplayCount
|
||||
{
|
||||
set { numberText.Text = value.ToString(@"#,0"); }
|
||||
}
|
||||
|
||||
public GradeBadge(string grade)
|
||||
{
|
||||
this.grade = grade;
|
||||
Width = width;
|
||||
Height = 41;
|
||||
Add(badge = new Sprite
|
||||
{
|
||||
Width = width,
|
||||
Height = 26
|
||||
});
|
||||
Add(numberText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-Bold"
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
badge.Texture = textures.Get($"Grades/{grade}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,11 +141,11 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
|
||||
break;
|
||||
|
||||
case RecentActivityType.UserSupportFirst:
|
||||
message = $"{userLinkTemplate()} has become an osu! supporter - thanks for your generosity!";
|
||||
message = $"{userLinkTemplate()} has become an osu!supporter - thanks for your generosity!";
|
||||
break;
|
||||
|
||||
case RecentActivityType.UserSupportGift:
|
||||
message = $"{userLinkTemplate()} has received the gift of osu! supporter!";
|
||||
message = $"{userLinkTemplate()} has received the gift of osu!supporter!";
|
||||
break;
|
||||
|
||||
case RecentActivityType.UsernameChange:
|
||||
|
||||
@@ -56,7 +56,13 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
|
||||
reloadSkins();
|
||||
|
||||
skinDropdown.Bindable = config.GetBindable<int>(OsuSetting.Skin);
|
||||
var skinBindable = config.GetBindable<int>(OsuSetting.Skin);
|
||||
|
||||
// Todo: This should not be necessary when OsuConfigManager is databased
|
||||
if (skinDropdown.Items.All(s => s.Value != skinBindable.Value))
|
||||
skinBindable.Value = 0;
|
||||
|
||||
skinDropdown.Bindable = skinBindable;
|
||||
}
|
||||
|
||||
private void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair<string, int>(s.ToString(), s.ID));
|
||||
|
||||
@@ -67,8 +67,8 @@ namespace osu.Game.Overlays.Toolbar
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(RulesetStore rulesets, OsuGame game)
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets, Bindable<RulesetInfo> parentRuleset)
|
||||
{
|
||||
this.rulesets = rulesets;
|
||||
foreach (var r in rulesets.AvailableRulesets)
|
||||
@@ -82,11 +82,7 @@ namespace osu.Game.Overlays.Toolbar
|
||||
|
||||
ruleset.ValueChanged += rulesetChanged;
|
||||
ruleset.DisabledChanged += disabledChanged;
|
||||
|
||||
if (game != null)
|
||||
ruleset.BindTo(game.Ruleset);
|
||||
else
|
||||
ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault();
|
||||
ruleset.BindTo(parentRuleset);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
|
||||
@@ -45,11 +45,15 @@ namespace osu.Game.Rulesets.Judgements
|
||||
public double TimeOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the <see cref="Result"/> should affect the combo portion of the score.
|
||||
/// If false, the <see cref="Result"/> will be considered for the bonus portion of the score.
|
||||
/// Whether the <see cref="Result"/> should affect the current combo.
|
||||
/// </summary>
|
||||
public virtual bool AffectsCombo => true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the <see cref="Result"/> should be counted as base (combo) or bonus score.
|
||||
/// </summary>
|
||||
public virtual bool IsBonus => !AffectsCombo;
|
||||
|
||||
/// <summary>
|
||||
/// The numeric representation for the result achieved.
|
||||
/// </summary>
|
||||
|
||||
@@ -10,7 +10,7 @@ using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Legacy
|
||||
{
|
||||
internal abstract class ConvertSlider : HitObject, IHasCurve
|
||||
internal abstract class ConvertSlider : HitObject, IHasCurve, IHasLegacyLastTickOffset
|
||||
{
|
||||
/// <summary>
|
||||
/// Scoring distance with a speed-adjusted beat length of 1 second.
|
||||
@@ -45,5 +45,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||
}
|
||||
|
||||
public double LegacyLastTickOffset => 36;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// A HitObject that is part of a combo and has extended information about its position relative to other combo objects.
|
||||
/// </summary>
|
||||
public interface IHasComboIndex : IHasCombo
|
||||
{
|
||||
/// <summary>
|
||||
/// The offset of this hitobject in the current combo.
|
||||
/// </summary>
|
||||
int IndexInCurrentCombo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The offset of this hitobject in the current combo.
|
||||
/// </summary>
|
||||
int ComboIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is the last object in the current combo.
|
||||
/// </summary>
|
||||
bool LastInCombo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="HitObject"/> which may require the last tick to be offset.
|
||||
/// This is specific to osu!stable conversion, and should not be used elsewhere.
|
||||
/// </summary>
|
||||
public interface IHasLegacyLastTickOffset
|
||||
{
|
||||
double LegacyLastTickOffset { get; }
|
||||
}
|
||||
}
|
||||
@@ -57,8 +57,18 @@ namespace osu.Game.Rulesets
|
||||
/// <returns></returns>
|
||||
public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="IBeatmapConverter"/> to convert a <see cref="IBeatmap"/> to one that is applicable for this <see cref="Ruleset"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The <see cref="IBeatmap"/> to be converted.</param>
|
||||
/// <returns>The <see cref="IBeatmapConverter"/>.</returns>
|
||||
public abstract IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap);
|
||||
|
||||
/// <summary>
|
||||
/// Optionally creates a <see cref="IBeatmapProcessor"/> to alter a <see cref="IBeatmap"/> after it has been converted.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The <see cref="IBeatmap"/> to be processed.</param>
|
||||
/// <returns>The <see cref="IBeatmapProcessor"/>.</returns>
|
||||
public virtual IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => null;
|
||||
|
||||
public abstract DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap);
|
||||
|
||||
@@ -84,10 +84,17 @@ namespace osu.Game.Rulesets
|
||||
{
|
||||
try
|
||||
{
|
||||
var instance = r.CreateInstance();
|
||||
var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo, asm =>
|
||||
{
|
||||
// for the time being, let's ignore the version being loaded.
|
||||
// this allows for debug builds to successfully load rulesets (even though debug rulesets have a 0.0.0 version).
|
||||
asm.Version = null;
|
||||
return Assembly.Load(asm);
|
||||
}, null), (RulesetInfo)null)).RulesetInfo;
|
||||
|
||||
r.Name = instance.Description;
|
||||
r.ShortName = instance.ShortName;
|
||||
r.Name = instanceInfo.Name;
|
||||
r.ShortName = instanceInfo.ShortName;
|
||||
r.InstantiationInfo = instanceInfo.InstantiationInfo;
|
||||
|
||||
r.Available = true;
|
||||
}
|
||||
|
||||
@@ -261,13 +261,19 @@ namespace osu.Game.Rulesets.Scoring
|
||||
break;
|
||||
}
|
||||
|
||||
baseScore += judgement.NumericResult;
|
||||
rollingMaxBaseScore += judgement.MaxNumericResult;
|
||||
|
||||
JudgedHits++;
|
||||
}
|
||||
else if (judgement.IsHit)
|
||||
bonusScore += judgement.NumericResult;
|
||||
|
||||
if (judgement.IsBonus)
|
||||
{
|
||||
if (judgement.IsHit)
|
||||
bonusScore += judgement.NumericResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
baseScore += judgement.NumericResult;
|
||||
rollingMaxBaseScore += judgement.MaxNumericResult;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -280,14 +286,18 @@ namespace osu.Game.Rulesets.Scoring
|
||||
HighestCombo.Value = judgement.HighestComboAtJudgement;
|
||||
|
||||
if (judgement.AffectsCombo)
|
||||
JudgedHits--;
|
||||
|
||||
if (judgement.IsBonus)
|
||||
{
|
||||
if (judgement.IsHit)
|
||||
bonusScore -= judgement.NumericResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
baseScore -= judgement.NumericResult;
|
||||
rollingMaxBaseScore -= judgement.MaxNumericResult;
|
||||
|
||||
JudgedHits--;
|
||||
}
|
||||
else if (judgement.IsHit)
|
||||
bonusScore -= judgement.NumericResult;
|
||||
}
|
||||
|
||||
private void updateScore()
|
||||
|
||||
@@ -4,30 +4,33 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="Playfield"/> specialized towards scrolling <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public abstract class ScrollingPlayfield : Playfield
|
||||
public abstract class ScrollingPlayfield : Playfield, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
/// <summary>
|
||||
/// The default span of time visible by the length of the scrolling axes.
|
||||
/// This is clamped between <see cref="time_span_min"/> and <see cref="time_span_max"/>.
|
||||
/// </summary>
|
||||
private const double time_span_default = 1500;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum span of time that may be visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_min = 50;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum span of time that may be visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_max = 10000;
|
||||
|
||||
/// <summary>
|
||||
/// The step increase/decrease of the span of time visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
@@ -78,27 +81,26 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
HitObjects.TimeRange.BindTo(VisibleTimeRange);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (!UserScrollSpeedAdjustment)
|
||||
return false;
|
||||
|
||||
if (state.Keyboard.ControlPressed)
|
||||
switch (action)
|
||||
{
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Minus:
|
||||
this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange + time_span_step, 600, Easing.OutQuint);
|
||||
break;
|
||||
case Key.Plus:
|
||||
this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange - time_span_step, 600, Easing.OutQuint);
|
||||
break;
|
||||
}
|
||||
case GlobalAction.IncreaseScrollSpeed:
|
||||
this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange - time_span_step, 200, Easing.OutQuint);
|
||||
return true;
|
||||
case GlobalAction.DecreaseScrollSpeed:
|
||||
this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange + time_span_step, 200, Easing.OutQuint);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
|
||||
protected sealed override HitObjectContainer CreateHitObjectContainer()
|
||||
{
|
||||
var container = new ScrollingHitObjectContainer();
|
||||
|
||||
@@ -40,7 +40,14 @@ namespace osu.Game.Screens
|
||||
while (screen.LoadState < LoadState.Ready)
|
||||
Thread.Sleep(1);
|
||||
|
||||
base.Push(screen);
|
||||
try
|
||||
{
|
||||
base.Push(screen);
|
||||
}
|
||||
catch (ScreenAlreadyExitedException)
|
||||
{
|
||||
// screen may have exited before the push was successful.
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
||||
@@ -52,8 +52,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
if (Beatmap.Value == null)
|
||||
return;
|
||||
|
||||
if (Beatmap.Value.Track.Length == double.PositiveInfinity) return;
|
||||
|
||||
float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
|
||||
adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user