mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 14:25:05 +08:00
Merge remote-tracking branch 'upstream/master' into take_screenshot
This commit is contained in:
commit
f944c29505
@ -1 +1 @@
|
||||
Subproject commit 92ec3d10b12c5e9bfc1d3b05d3db174a506efd6d
|
||||
Subproject commit 7bb0782200abadf73b79ed1a3bc1d5b926c6a81e
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
|
||||
{
|
||||
if (beatmap.ComboColors.Count == 0)
|
||||
if (beatmap.ComboColours.Count == 0)
|
||||
return;
|
||||
|
||||
int index = 0;
|
||||
@ -31,11 +31,11 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
if (obj.NewCombo)
|
||||
{
|
||||
if (lastObj != null) lastObj.LastInCombo = true;
|
||||
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
|
||||
colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count;
|
||||
}
|
||||
|
||||
obj.IndexInBeatmap = index++;
|
||||
obj.ComboColour = beatmap.ComboColors[colourIndex];
|
||||
obj.ComboColour = beatmap.ComboColours[colourIndex];
|
||||
|
||||
lastObj = obj;
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModDaycore : ModDaycore
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
}
|
||||
}
|
||||
|
@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModEasy : ModEasy
|
||||
{
|
||||
public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModHalfTime : ModHalfTime
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
public class CatchModHardRock : ModHardRock
|
||||
{
|
||||
public override double ScoreMultiplier => 1.12;
|
||||
public override bool Ranked => true;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModHidden : ModHidden
|
||||
{
|
||||
public override string Description => @"Play with fading notes for a slight score advantage.";
|
||||
public override string Description => @"Play with fading fruits.";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
Origin = Anchor.BottomLeft;
|
||||
X = 0;
|
||||
|
||||
Child = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
|
||||
InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
foreach (var b in s.NestedHitObjects.Cast<BananaShower.Banana>())
|
||||
AddNested(getVisualRepresentation?.Invoke(b));
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Child = new Pulp
|
||||
InternalChild = new Pulp
|
||||
{
|
||||
AccentColour = AccentColour,
|
||||
Size = Size
|
||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new[]
|
||||
InternalChildren = new[]
|
||||
{
|
||||
createPulp(HitObject.VisualRepresentation),
|
||||
border = new Circle
|
||||
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
|
||||
if (HitObject.HyperDash)
|
||||
{
|
||||
Add(new Pulp
|
||||
AddInternal(new Pulp
|
||||
{
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
Origin = Anchor.BottomLeft;
|
||||
X = 0;
|
||||
|
||||
Child = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
|
||||
InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
|
||||
|
||||
foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
|
||||
AddNested(getVisualRepresentation?.Invoke(o));
|
||||
|
@ -65,54 +65,59 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
X = X
|
||||
});
|
||||
|
||||
for (var span = 0; span < this.SpanCount(); span++)
|
||||
double lastDropletTime = StartTime;
|
||||
|
||||
for (int span = 0; span < this.SpanCount(); span++)
|
||||
{
|
||||
var spanStartTime = StartTime + span * spanDuration;
|
||||
var reversed = span % 2 == 1;
|
||||
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
for (double d = 0; d <= length; d += tickDistance)
|
||||
{
|
||||
if (d > length - minDistanceFromEnd)
|
||||
break;
|
||||
|
||||
var timeProgress = d / length;
|
||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||
|
||||
var lastTickTime = spanStartTime + timeProgress * spanDuration;
|
||||
AddNested(new Droplet
|
||||
double time = spanStartTime + timeProgress * spanDuration;
|
||||
|
||||
double tinyTickInterval = time - lastDropletTime;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
|
||||
{
|
||||
StartTime = lastTickTime,
|
||||
ComboColour = ComboColour,
|
||||
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
StartTime = t,
|
||||
ComboColour = ComboColour,
|
||||
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
double tinyTickInterval = tickDistance / length * spanDuration;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = 0; t < spanDuration; t += tinyTickInterval)
|
||||
{
|
||||
double progress = reversed ? 1 - t / spanDuration : t / spanDuration;
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
|
||||
{
|
||||
StartTime = spanStartTime + t,
|
||||
ComboColour = ComboColour,
|
||||
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
AddNested(new Droplet
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
StartTime = time,
|
||||
ComboColour = ComboColour,
|
||||
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
lastDropletTime = time;
|
||||
}
|
||||
|
||||
AddNested(new Fruit
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||
|
||||
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2149")]
|
||||
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModDaycore : ModDaycore
|
||||
{
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModDoubleTime : ModDoubleTime
|
||||
{
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
public override double ScoreMultiplier => 1;
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override string Name => "Dual Stages";
|
||||
public override string ShortenedName => "DS";
|
||||
public override string Description => @"Double the stages, double the fun!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => false;
|
||||
public override double ScoreMultiplier => 0;
|
||||
|
||||
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
|
||||
{
|
||||
|
@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModEasy : ModEasy
|
||||
{
|
||||
public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!";
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,11 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModFadeIn : Mod
|
||||
{
|
||||
public override string Name => "FadeIn";
|
||||
public override string Name => "Fade In";
|
||||
public override string ShortenedName => "FI";
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
public override string Description => @"Keys appear out of nowhere!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => true;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
|
||||
|
@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModFlashlight : ModFlashlight
|
||||
{
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModHidden) };
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModHalfTime : ModHalfTime
|
||||
{
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModHardRock : ModHardRock
|
||||
{
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
public override double ScoreMultiplier => 1;
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModHidden : ModHidden
|
||||
{
|
||||
public override string Description => @"The notes fade out before you hit them!";
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
public override string Description => @"Keys fade out before you hit them!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public class ManiaModKey1 : ManiaKeyMod
|
||||
{
|
||||
public override int KeyCount => 1;
|
||||
public override string Name => "1K";
|
||||
public override string Name => "One Key";
|
||||
public override string ShortenedName => "1K";
|
||||
public override string Description => @"Play with one key.";
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public class ManiaModKey2 : ManiaKeyMod
|
||||
{
|
||||
public override int KeyCount => 2;
|
||||
public override string Name => "2K";
|
||||
public override string Name => "Two Keys";
|
||||
public override string ShortenedName => "2K";
|
||||
public override string Description => @"Play with two keys.";
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public class ManiaModKey3 : ManiaKeyMod
|
||||
{
|
||||
public override int KeyCount => 3;
|
||||
public override string Name => "3K";
|
||||
public override string Name => "Three Keys";
|
||||
public override string ShortenedName => "3K";
|
||||
public override string Description => @"Play with three keys.";
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public class ManiaModKey4 : ManiaKeyMod
|
||||
{
|
||||
public override int KeyCount => 4;
|
||||
public override string Name => "4K";
|
||||
public override string Name => "Four Keys";
|
||||
public override string ShortenedName => "4K";
|
||||
public override string Description => @"Play with four keys.";
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public class ManiaModKey5 : ManiaKeyMod
|
||||
{
|
||||
public override int KeyCount => 5;
|
||||
public override string Name => "5K";
|
||||
public override string Name => "Five Keys";
|
||||
public override string ShortenedName => "5K";
|
||||
public override string Description => @"Play with five keys.";
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public class ManiaModKey6 : ManiaKeyMod
|
||||
{
|
||||
public override int KeyCount => 6;
|
||||
public override string Name => "6K";
|
||||
public override string Name => "Six Keys";
|
||||
public override string ShortenedName => "6K";
|
||||
public override string Description => @"Play with six keys.";
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public class ManiaModKey7 : ManiaKeyMod
|
||||
{
|
||||
public override int KeyCount => 7;
|
||||
public override string Name => "7K";
|
||||
public override string Name => "Seven Keys";
|
||||
public override string ShortenedName => "7K";
|
||||
public override string Description => @"Play with seven keys.";
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public class ManiaModKey8 : ManiaKeyMod
|
||||
{
|
||||
public override int KeyCount => 8;
|
||||
public override string Name => "8K";
|
||||
public override string Name => "Eight Keys";
|
||||
public override string ShortenedName => "8K";
|
||||
public override string Description => @"Play with eight keys.";
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public class ManiaModKey9 : ManiaKeyMod
|
||||
{
|
||||
public override int KeyCount => 9;
|
||||
public override string Name => "9K";
|
||||
public override string Name => "Nine Keys";
|
||||
public override string ShortenedName => "9K";
|
||||
public override string Description => @"Play with nine keys.";
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModNightcore : ModNightcore
|
||||
{
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
public override double ScoreMultiplier => 1;
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override string Name => "Random";
|
||||
public override string ShortenedName => "RD";
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
|
||||
public override string Description => @"Shuffle around the notes!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override string Description => @"Shuffle around the keys!";
|
||||
public override double ScoreMultiplier => 0;
|
||||
|
||||
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 1;
|
||||
|
||||
Add(new Box
|
||||
AddInternal(new Box
|
||||
{
|
||||
Name = "Bar line",
|
||||
Anchor = Anchor.BottomCentre,
|
||||
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
if (isMajor)
|
||||
{
|
||||
Add(new EquilateralTriangle
|
||||
AddInternal(new EquilateralTriangle
|
||||
{
|
||||
Name = "Left triangle",
|
||||
Anchor = Anchor.BottomLeft,
|
||||
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
Rotation = 90
|
||||
});
|
||||
|
||||
Add(new EquilateralTriangle
|
||||
AddInternal(new EquilateralTriangle
|
||||
{
|
||||
Name = "Right triangle",
|
||||
Anchor = Anchor.BottomRight,
|
||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
AddRange(new Drawable[]
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
// The hit object itself cannot be used for various elements because the tail overshoots it
|
||||
// So a specialized container that is updated to contain the tail height is used
|
||||
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
foreach (var tick in HitObject.NestedHitObjects.OfType<HoldNoteTick>())
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Size = new Vector2(1);
|
||||
|
||||
Children = new[]
|
||||
InternalChildren = new[]
|
||||
{
|
||||
glowContainer = new CircularContainer
|
||||
{
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Children = new Drawable[]
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
laneGlowPiece = new LaneGlowPiece
|
||||
{
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
{
|
||||
applyStacking(beatmap);
|
||||
|
||||
if (beatmap.ComboColors.Count == 0)
|
||||
if (beatmap.ComboColours.Count == 0)
|
||||
return;
|
||||
|
||||
int comboIndex = 0;
|
||||
@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
if (obj.NewCombo)
|
||||
{
|
||||
comboIndex = 0;
|
||||
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
|
||||
colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count;
|
||||
}
|
||||
|
||||
obj.IndexInCurrentCombo = comboIndex++;
|
||||
obj.ComboColour = beatmap.ComboColors[colourIndex];
|
||||
obj.ComboColour = beatmap.ComboColours[colourIndex];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModDaycore : ModDaycore
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
}
|
||||
}
|
||||
|
@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModEasy : ModEasy
|
||||
{
|
||||
public override string Description => @"Larger circles, more forgiving HP drain, less accuracy required, and three lives!";
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModHalfTime : ModHalfTime
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject>
|
||||
{
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
public override bool Ranked => true;
|
||||
|
||||
public void ApplyToHitObject(OsuHitObject hitObject)
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects
|
||||
{
|
||||
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage.";
|
||||
public override string Description => @"Play with no approach circles and fading circles/sliders.";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
|
||||
private const double fade_in_duration_multiplier = 0.4;
|
||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModRelax : ModRelax
|
||||
{
|
||||
public override string Description => "You don't need to click.\nGive your clicking/tapping finger a break from the heat of things.";
|
||||
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Name => "Spun Out";
|
||||
public override string ShortenedName => "SO";
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
|
||||
public override string Description => @"Spinners will be automatically completed";
|
||||
public override string Description => @"Spinners will be automatically completed.";
|
||||
public override double ScoreMultiplier => 0.9;
|
||||
public override bool Ranked => true;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) };
|
||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Name => "Target";
|
||||
public override string ShortenedName => "TP";
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
|
||||
public override string Description => @"";
|
||||
public override string Description => @"Practice keeping up with the beat of the song.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Position = HitObject.StackedPosition;
|
||||
Scale = new Vector2(h.Scale);
|
||||
|
||||
Children = new Drawable[]
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
glow = new GlowPiece
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Blending = BlendingMode.Additive;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Children = new Drawable[]
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Container<DrawableSliderTick> ticks;
|
||||
Container<DrawableRepeatPoint> repeatPoints;
|
||||
|
||||
Children = new Drawable[]
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
Body = new SliderBody(s)
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
BorderThickness = 2;
|
||||
BorderColour = Color4.White;
|
||||
|
||||
Children = new Drawable[]
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
Spinner = s;
|
||||
|
||||
Children = new Drawable[]
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
circleContainer = new Container
|
||||
{
|
||||
|
33
osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs
Normal file
33
osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs
Normal file
@ -0,0 +1,33 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseGameplayCursor : OsuTestCase, IProvideCursor
|
||||
{
|
||||
private GameplayCursor cursor;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new [] { typeof(CursorTrail) };
|
||||
|
||||
public CursorContainer Cursor => cursor;
|
||||
|
||||
public bool ProvidingUserCursor => true;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(cursor = new GameplayCursor { RelativeSizeAxes = Axes.Both });
|
||||
}
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.OpenGL.Buffers;
|
||||
using osu.Framework.Graphics.OpenGL.Vertices;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
@ -14,11 +14,12 @@ using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Timing;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Graphics.ES30;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
{
|
||||
internal class CursorTrail : Drawable
|
||||
internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition
|
||||
{
|
||||
private int currentIndex;
|
||||
|
||||
@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
|
||||
private float time;
|
||||
|
||||
public override bool IsPresent => true;
|
||||
|
||||
private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData();
|
||||
private const int max_sprites = 2048;
|
||||
|
||||
@ -96,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
|
||||
const int fade_clock_reset_threshold = 1000000;
|
||||
|
||||
time = (float)(Time.Current - timeOffset) / 500f;
|
||||
time = (float)(Time.Current - timeOffset) / 300f;
|
||||
if (time > fade_clock_reset_threshold)
|
||||
resetTime();
|
||||
}
|
||||
@ -115,14 +118,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
{
|
||||
Vector2 pos = state.Mouse.NativeState.Position;
|
||||
|
||||
if (lastPosition == null)
|
||||
{
|
||||
lastPosition = state.Mouse.NativeState.Position;
|
||||
lastPosition = pos;
|
||||
resampler.AddPosition(lastPosition.Value);
|
||||
return base.OnMouseMove(state);
|
||||
}
|
||||
|
||||
foreach (Vector2 pos2 in resampler.AddPosition(state.Mouse.NativeState.Position))
|
||||
foreach (Vector2 pos2 in resampler.AddPosition(pos))
|
||||
{
|
||||
Trace.Assert(lastPosition.HasValue);
|
||||
|
||||
@ -162,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
|
||||
private class TrailDrawNodeSharedData
|
||||
{
|
||||
public VertexBuffer<TexturedVertex2D> VertexBuffer;
|
||||
public VertexBuffer<TexturedTrailVertex> VertexBuffer;
|
||||
}
|
||||
|
||||
private class TrailDrawNode : DrawNode
|
||||
@ -188,7 +193,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
public override void Draw(Action<TexturedVertex2D> vertexAction)
|
||||
{
|
||||
if (Shared.VertexBuffer == null)
|
||||
Shared.VertexBuffer = new QuadVertexBuffer<TexturedVertex2D>(max_sprites, BufferUsageHint.DynamicDraw);
|
||||
Shared.VertexBuffer = new QuadVertexBuffer<TexturedTrailVertex>(max_sprites, BufferUsageHint.DynamicDraw);
|
||||
|
||||
Shader.GetUniform<float>("g_FadeClock").Value = Time;
|
||||
|
||||
@ -205,17 +210,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
int end = start;
|
||||
|
||||
Vector2 pos = Parts[i].Position;
|
||||
ColourInfo colour = DrawInfo.Colour;
|
||||
colour.TopLeft.Linear.A = Parts[i].Time + colour.TopLeft.Linear.A;
|
||||
colour.TopRight.Linear.A = Parts[i].Time + colour.TopRight.Linear.A;
|
||||
colour.BottomLeft.Linear.A = Parts[i].Time + colour.BottomLeft.Linear.A;
|
||||
colour.BottomRight.Linear.A = Parts[i].Time + colour.BottomRight.Linear.A;
|
||||
float time = Parts[i].Time;
|
||||
|
||||
Texture.DrawQuad(
|
||||
new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y),
|
||||
colour,
|
||||
DrawInfo.Colour,
|
||||
null,
|
||||
v => Shared.VertexBuffer.Vertices[end++] = v);
|
||||
v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex
|
||||
{
|
||||
Position = v.Position,
|
||||
TexturePosition = v.TexturePosition,
|
||||
Time = time + 1,
|
||||
Colour = v.Colour,
|
||||
});
|
||||
|
||||
Parts[i].WasUpdated = false;
|
||||
}
|
||||
@ -240,5 +247,26 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
Shader.Unbind();
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct TexturedTrailVertex : IEquatable<TexturedTrailVertex>, IVertex
|
||||
{
|
||||
[VertexMember(2, VertexAttribPointerType.Float)]
|
||||
public Vector2 Position;
|
||||
[VertexMember(4, VertexAttribPointerType.Float)]
|
||||
public Color4 Colour;
|
||||
[VertexMember(2, VertexAttribPointerType.Float)]
|
||||
public Vector2 TexturePosition;
|
||||
[VertexMember(1, VertexAttribPointerType.Float)]
|
||||
public float Time;
|
||||
|
||||
public bool Equals(TexturedTrailVertex other)
|
||||
{
|
||||
return Position.Equals(other.Position)
|
||||
&& TexturePosition.Equals(other.TexturePosition)
|
||||
&& Colour.Equals(other.Colour)
|
||||
&& Time.Equals(other.Time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,13 +20,66 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
{
|
||||
protected override Drawable CreateCursor() => new OsuCursor();
|
||||
|
||||
protected override Container<Drawable> Content => fadeContainer;
|
||||
|
||||
private readonly Container<Drawable> fadeContainer;
|
||||
|
||||
public GameplayCursor()
|
||||
{
|
||||
Add(new CursorTrail { Depth = 1 });
|
||||
InternalChild = fadeContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new CursorTrail { Depth = 1 }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int downCount;
|
||||
|
||||
public bool OnPressed(OsuAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
downCount++;
|
||||
ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(OsuAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
if (--downCount == 0)
|
||||
ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
fadeContainer.FadeTo(1, 300, Easing.OutQuint);
|
||||
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
|
||||
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public class OsuCursor : Container
|
||||
{
|
||||
private Container cursorContainer;
|
||||
@ -131,45 +184,5 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
cursorContainer.Scale = new Vector2(scale);
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(OsuAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
downCount++;
|
||||
ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(OsuAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
if (--downCount == 0)
|
||||
ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
ActiveCursor.FadeTo(1, 250, Easing.OutQuint);
|
||||
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
ActiveCursor.FadeTo(0, 250, Easing.OutQuint);
|
||||
ActiveCursor.ScaleTo(0.6f, 250, Easing.In);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +131,7 @@
|
||||
<Compile Include="Replays\OsuReplayInputHandler.cs" />
|
||||
<Compile Include="Tests\OsuBeatmapConversionTest.cs" />
|
||||
<Compile Include="Tests\TestCaseEditor.cs" />
|
||||
<Compile Include="Tests\TestCaseGameplayCursor.cs" />
|
||||
<Compile Include="Tests\TestCaseHitCircle.cs" />
|
||||
<Compile Include="Tests\TestCaseHitCircleHidden.cs" />
|
||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
public class TaikoModDaycore : ModDaycore
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
}
|
||||
}
|
||||
|
@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
public class TaikoModEasy : ModEasy
|
||||
{
|
||||
public override string Description => @"Beats move slower, less accuracy required, and three lives!";
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
public class TaikoModHalfTime : ModHalfTime
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
public class TaikoModHardRock : ModHardRock
|
||||
{
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
public override bool Ranked => true;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
public class TaikoModHidden : ModHidden
|
||||
{
|
||||
public override string Description => @"The notes fade out before you hit them!";
|
||||
public override string Description => @"Beats fade out before you hit them!";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
public class TaikoModRelax : ModRelax
|
||||
{
|
||||
public override string Description => @"Relax! You will no longer get dizzyfied by ninja-like spinners, demanding drumrolls or unexpected katu's.";
|
||||
public override string Description => @"No ninja-like spinners, demanding drumrolls or unexpected katu's.";
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Width = tracker_width;
|
||||
|
||||
Children = new[]
|
||||
InternalChildren = new[]
|
||||
{
|
||||
Tracker = new Box
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
public DrawableBarLineMajor(BarLine barLine)
|
||||
: base(barLine)
|
||||
{
|
||||
Add(triangleContainer = new Container
|
||||
AddInternal(triangleContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
Content.ScaleTo(0, 100, Easing.OutQuint).Expire();
|
||||
this.ScaleTo(0, 100, Easing.OutQuint).Expire();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
const float gravity_time = 300;
|
||||
const float gravity_travel_height = 200;
|
||||
|
||||
Content.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
|
||||
this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
|
||||
|
||||
this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out)
|
||||
.Then()
|
||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
FillMode = FillMode.Fit;
|
||||
|
||||
Add(bodyContainer = new Container
|
||||
AddInternal(bodyContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Depth = 1,
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE);
|
||||
|
||||
Add(MainPiece = CreateMainPiece());
|
||||
InternalChild = MainPiece = CreateMainPiece();
|
||||
MainPiece.KiaiMode = HitObject.Kiai;
|
||||
}
|
||||
|
||||
|
@ -167,7 +167,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var comboColors = decoder.Decode(stream).ComboColors;
|
||||
var comboColors = decoder.Decode(stream).ComboColours;
|
||||
|
||||
Color4[] expectedColors =
|
||||
{
|
||||
|
@ -102,9 +102,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
new Color4(255, 187, 255, 255),
|
||||
new Color4(255, 177, 140, 255),
|
||||
};
|
||||
Assert.AreEqual(expected.Length, beatmap.ComboColors.Count);
|
||||
Assert.AreEqual(expected.Length, beatmap.ComboColours.Count);
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
Assert.AreEqual(expected[i], beatmap.ComboColors[i]);
|
||||
Assert.AreEqual(expected[i], beatmap.ComboColours[i]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -1,48 +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 System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Edit.Screens.Compose;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseEditorCompose : OsuTestCase
|
||||
{
|
||||
private readonly Random random;
|
||||
private readonly Compose compose;
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
public TestCaseEditorCompose()
|
||||
{
|
||||
random = new Random(1337);
|
||||
|
||||
Add(compose = new Compose());
|
||||
AddStep("Next beatmap", nextBeatmap);
|
||||
}
|
||||
|
||||
private OsuGameBase osuGame;
|
||||
private BeatmapManager beatmaps;
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osuGame, BeatmapManager beatmaps)
|
||||
private void load(OsuGameBase osuGame)
|
||||
{
|
||||
this.osuGame = osuGame;
|
||||
this.beatmaps = beatmaps;
|
||||
osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
|
||||
var compose = new Compose();
|
||||
compose.Beatmap.BindTo(osuGame.Beatmap);
|
||||
}
|
||||
|
||||
private void nextBeatmap()
|
||||
{
|
||||
var sets = beatmaps.GetAllUsableBeatmapSets();
|
||||
if (sets.Count == 0)
|
||||
return;
|
||||
|
||||
var b = sets[random.Next(0, sets.Count)].Beatmaps[0];
|
||||
osuGame.Beatmap.Value = beatmaps.GetWorkingBeatmap(b);
|
||||
Child = compose;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
464
osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs
Normal file
464
osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs
Normal file
@ -0,0 +1,464 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
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;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseEditorSeekSnapping : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(HitObjectComposer) };
|
||||
|
||||
private Track track;
|
||||
private HitObjectComposer composer;
|
||||
|
||||
private DecoupleableInterpolatingFramedClock clock;
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osuGame)
|
||||
{
|
||||
clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
|
||||
var testBeatmap = new Beatmap
|
||||
{
|
||||
ControlPointInfo = new ControlPointInfo
|
||||
{
|
||||
TimingPoints =
|
||||
{
|
||||
new TimingControlPoint { Time = 0, BeatLength = 200},
|
||||
new TimingControlPoint { Time = 100, BeatLength = 400 },
|
||||
new TimingControlPoint { Time = 175, BeatLength = 800 },
|
||||
new TimingControlPoint { Time = 350, BeatLength = 200 },
|
||||
new TimingControlPoint { Time = 450, BeatLength = 100 },
|
||||
new TimingControlPoint { Time = 500, BeatLength = 307.69230769230802 }
|
||||
}
|
||||
},
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = 0 },
|
||||
new HitCircle { StartTime = 5000 }
|
||||
}
|
||||
};
|
||||
|
||||
osuGame.Beatmap.Value = new TestWorkingBeatmap(testBeatmap);
|
||||
track = osuGame.Beatmap.Value.Track;
|
||||
|
||||
Child = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[] { composer = new TestHitObjectComposer(new OsuRuleset()) },
|
||||
new Drawable[] { new TimingPointVisualiser(testBeatmap, track) { Clock = clock } },
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.Distributed),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
}
|
||||
};
|
||||
|
||||
testSeekNoSnapping();
|
||||
testSeekSnappingOnBeat();
|
||||
testSeekSnappingInBetweenBeat();
|
||||
testSeekForwardNoSnapping();
|
||||
testSeekForwardSnappingOnBeat();
|
||||
testSeekForwardSnappingFromInBetweenBeat();
|
||||
testSeekBackwardSnappingOnBeat();
|
||||
testSeekBackwardSnappingFromInBetweenBeat();
|
||||
testSeekingWithFloatingPointBeatLength();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether time is correctly seeked without snapping.
|
||||
/// </summary>
|
||||
private void testSeekNoSnapping()
|
||||
{
|
||||
reset();
|
||||
|
||||
// Forwards
|
||||
AddStep("Seek(0)", () => composer.SeekTo(0));
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
AddStep("Seek(33)", () => composer.SeekTo(33));
|
||||
AddAssert("Time = 33", () => clock.CurrentTime == 33);
|
||||
AddStep("Seek(89)", () => composer.SeekTo(89));
|
||||
AddAssert("Time = 89", () => clock.CurrentTime == 89);
|
||||
|
||||
// Backwards
|
||||
AddStep("Seek(25)", () => composer.SeekTo(25));
|
||||
AddAssert("Time = 25", () => clock.CurrentTime == 25);
|
||||
AddStep("Seek(0)", () => composer.SeekTo(0));
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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()
|
||||
{
|
||||
reset();
|
||||
|
||||
AddStep("Seek(0), Snap", () => composer.SeekTo(0, true));
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
AddStep("Seek(50), Snap", () => composer.SeekTo(50, true));
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("Seek(100), Snap", () => composer.SeekTo(100, true));
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("Seek(175), Snap", () => composer.SeekTo(175, true));
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("Seek(350), Snap", () => composer.SeekTo(350, true));
|
||||
AddAssert("Time = 350", () => clock.CurrentTime == 350);
|
||||
AddStep("Seek(400), Snap", () => composer.SeekTo(400, true));
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("Seek(450), Snap", () => composer.SeekTo(450, true));
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
}
|
||||
|
||||
/// <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()
|
||||
{
|
||||
reset();
|
||||
|
||||
AddStep("Seek(24), Snap", () => composer.SeekTo(24, true));
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
AddStep("Seek(26), Snap", () => composer.SeekTo(26, true));
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("Seek(150), Snap", () => composer.SeekTo(150, true));
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("Seek(170), Snap", () => composer.SeekTo(170, true));
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("Seek(274), Snap", () => composer.SeekTo(274, true));
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("Seek(276), Snap", () => composer.SeekTo(276, true));
|
||||
AddAssert("Time = 350", () => clock.CurrentTime == 350);
|
||||
}
|
||||
|
||||
/// <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()
|
||||
{
|
||||
reset();
|
||||
|
||||
AddStep("SeekForward", () => composer.SeekForward());
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("SeekForward", () => composer.SeekForward());
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("SeekForward", () => composer.SeekForward());
|
||||
AddAssert("Time = 200", () => clock.CurrentTime == 200);
|
||||
AddStep("SeekForward", () => composer.SeekForward());
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("SeekForward", () => composer.SeekForward());
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that when seeking forward with beat snapping, all beats are snapped to and timing points are never skipped.
|
||||
/// </summary>
|
||||
private void testSeekForwardSnappingOnBeat()
|
||||
{
|
||||
reset();
|
||||
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 350", () => clock.CurrentTime == 350);
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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()
|
||||
{
|
||||
reset();
|
||||
|
||||
AddStep("Seek(49)", () => composer.SeekTo(49));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("Seek(49.999)", () => composer.SeekTo(49.999));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("Seek(99)", () => composer.SeekTo(99));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("Seek(99.999)", () => composer.SeekTo(99.999));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("Seek(174)", () => composer.SeekTo(174));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("Seek(349)", () => composer.SeekTo(349));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 350", () => clock.CurrentTime == 350);
|
||||
AddStep("Seek(399)", () => composer.SeekTo(399));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("Seek(449)", () => composer.SeekTo(449));
|
||||
AddStep("SeekForward, Snap", () => composer.SeekForward(true));
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
}
|
||||
|
||||
/// <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()
|
||||
{
|
||||
reset();
|
||||
|
||||
AddStep("Seek(450)", () => composer.SeekTo(450));
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 425", () => clock.CurrentTime == 425);
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 375", () => clock.CurrentTime == 375);
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 325", () => clock.CurrentTime == 325);
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 125", () => clock.CurrentTime == 125);
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 25", () => clock.CurrentTime == 25);
|
||||
AddStep("SeekBackward", () => composer.SeekBackward());
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that when seeking backward with beat snapping, all beats are snapped to and timing points are never skipped.
|
||||
/// </summary>
|
||||
private void testSeekBackwardSnappingOnBeat()
|
||||
{
|
||||
reset();
|
||||
|
||||
AddStep("Seek(450)", () => composer.SeekTo(450));
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 350", () => clock.CurrentTime == 350);
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 175", () => clock.CurrentTime == 175);
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 100", () => clock.CurrentTime == 100);
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 50", () => clock.CurrentTime == 50);
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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()
|
||||
{
|
||||
reset();
|
||||
|
||||
AddStep("Seek(451)", () => composer.SeekTo(451));
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
AddStep("Seek(450.999)", () => composer.SeekTo(450.999));
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 450", () => clock.CurrentTime == 450);
|
||||
AddStep("Seek(401)", () => composer.SeekTo(401));
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
AddStep("Seek(401.999)", () => composer.SeekTo(401.999));
|
||||
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true));
|
||||
AddAssert("Time = 400", () => clock.CurrentTime == 400);
|
||||
}
|
||||
|
||||
/// <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()
|
||||
{
|
||||
reset();
|
||||
|
||||
double lastTime = 0;
|
||||
|
||||
AddStep("Seek(0)", () => composer.SeekTo(0));
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
AddStep("SeekForward, Snap", () =>
|
||||
{
|
||||
lastTime = clock.CurrentTime;
|
||||
composer.SeekForward(true);
|
||||
});
|
||||
AddAssert("Time > lastTime", () => clock.CurrentTime > lastTime);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
AddStep("SeekBackward, Snap", () =>
|
||||
{
|
||||
lastTime = clock.CurrentTime;
|
||||
composer.SeekBackward(true);
|
||||
});
|
||||
AddAssert("Time < lastTime", () => clock.CurrentTime < lastTime);
|
||||
}
|
||||
|
||||
AddAssert("Time = 0", () => clock.CurrentTime == 0);
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
AddStep("Reset", () => composer.SeekTo(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 Track track;
|
||||
|
||||
private readonly Drawable tracker;
|
||||
|
||||
public TimingPointVisualiser(Beatmap beatmap, Track track)
|
||||
{
|
||||
this.track = track;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Width = 0.75f;
|
||||
|
||||
FillFlowContainer timelineContainer;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Name = "Background",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(85f)
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = "Tracks",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding(15),
|
||||
Children = new[]
|
||||
{
|
||||
tracker = new Box
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
RelativePositionAxes = Axes.X,
|
||||
Width = 2,
|
||||
Colour = Color4.Red,
|
||||
},
|
||||
timelineContainer = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0, 5)
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var timingPoints = beatmap.ControlPointInfo.TimingPoints;
|
||||
|
||||
for (int i = 0; i < timingPoints.Count; i++)
|
||||
{
|
||||
TimingControlPoint next = i == timingPoints.Count - 1 ? null : timingPoints[i + 1];
|
||||
timelineContainer.Add(new TimingPointTimeline(timingPoints[i], next?.Time ?? beatmap.HitObjects.Last().StartTime, track.Length));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
tracker.X = (float)(Time.Current / track.Length);
|
||||
}
|
||||
|
||||
private class TimingPointTimeline : CompositeDrawable
|
||||
{
|
||||
public TimingPointTimeline(TimingControlPoint timingPoint, double endTime, double fullDuration)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Box createMainTick(double time) => new Box
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = (float)(time / fullDuration),
|
||||
Height = 10,
|
||||
Width = 2
|
||||
};
|
||||
|
||||
Box createBeatTick(double time) => new Box
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = (float)(time / fullDuration),
|
||||
Height = 5,
|
||||
Width = 2,
|
||||
Colour = time > endTime ? Color4.Gray : Color4.Yellow
|
||||
};
|
||||
|
||||
AddInternal(createMainTick(timingPoint.Time));
|
||||
AddInternal(createMainTick(endTime));
|
||||
|
||||
for (double t = timingPoint.Time + timingPoint.BeatLength / 4; t < fullDuration; t += timingPoint.BeatLength / 4)
|
||||
AddInternal(createBeatTick(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Timing;
|
||||
using OpenTK;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -34,6 +35,11 @@ namespace osu.Game.Tests.Visual
|
||||
typeof(SliderCircleMask)
|
||||
};
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osuGame)
|
||||
{
|
||||
@ -59,6 +65,10 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
});
|
||||
|
||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
|
||||
Child = new OsuHitObjectComposer(new OsuRuleset());
|
||||
}
|
||||
}
|
||||
|
@ -4,30 +4,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using OpenTK;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseEditorSummaryTimeline : OsuTestCase
|
||||
{
|
||||
private const int length = 60000;
|
||||
private readonly Random random;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(SummaryTimeline) };
|
||||
|
||||
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
public TestCaseEditorSummaryTimeline()
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
random = new Random(1337);
|
||||
beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
|
||||
SummaryTimeline summaryTimeline;
|
||||
Add(summaryTimeline = new SummaryTimeline
|
||||
@ -38,58 +46,6 @@ namespace osu.Game.Tests.Visual
|
||||
});
|
||||
|
||||
summaryTimeline.Beatmap.BindTo(beatmap);
|
||||
|
||||
AddStep("New beatmap", newBeatmap);
|
||||
|
||||
newBeatmap();
|
||||
}
|
||||
|
||||
private void newBeatmap()
|
||||
{
|
||||
var b = new Beatmap();
|
||||
|
||||
for (int i = 0; i < random.Next(1, 10); i++)
|
||||
b.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { Time = random.Next(0, length) });
|
||||
|
||||
for (int i = 0; i < random.Next(1, 5); i++)
|
||||
b.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint { Time = random.Next(0, length) });
|
||||
|
||||
for (int i = 0; i < random.Next(1, 5); i++)
|
||||
b.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = random.Next(0, length) });
|
||||
|
||||
for (int i = 0; i < random.Next(1, 5); i++)
|
||||
b.ControlPointInfo.SamplePoints.Add(new SampleControlPoint { Time = random.Next(0, length) });
|
||||
|
||||
b.BeatmapInfo.Bookmarks = new int[random.Next(10, 30)];
|
||||
for (int i = 0; i < b.BeatmapInfo.Bookmarks.Length; i++)
|
||||
b.BeatmapInfo.Bookmarks[i] = random.Next(0, length);
|
||||
|
||||
beatmap.Value = new TestWorkingBeatmap(b);
|
||||
}
|
||||
|
||||
private class TestWorkingBeatmap : WorkingBeatmap
|
||||
{
|
||||
private readonly Beatmap beatmap;
|
||||
|
||||
public TestWorkingBeatmap(Beatmap beatmap)
|
||||
: base(beatmap.BeatmapInfo)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
protected override Texture GetBackground() => null;
|
||||
|
||||
protected override Beatmap GetBeatmap() => beatmap;
|
||||
|
||||
protected override Track GetTrack() => new TestTrack();
|
||||
|
||||
private class TestTrack : TrackVirtual
|
||||
{
|
||||
public TestTrack()
|
||||
{
|
||||
Length = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
@ -13,17 +15,28 @@ namespace osu.Game.Tests.Visual
|
||||
[TestFixture]
|
||||
public class TestCasePlaybackControl : OsuTestCase
|
||||
{
|
||||
public TestCasePlaybackControl()
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
|
||||
var playback = new PlaybackControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200,100)
|
||||
};
|
||||
|
||||
playback.Beatmap.Value = new TestWorkingBeatmap(new Beatmap());
|
||||
|
||||
Add(playback);
|
||||
Child = playback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,12 +140,12 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Add(new Box
|
||||
InternalChild = new Box
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
});
|
||||
};
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
@ -175,7 +175,7 @@ namespace osu.Game.Tests.Visual
|
||||
Origin = Anchor.Centre;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Add(new Box { Size = new Vector2(75) });
|
||||
InternalChild = new Box { Size = new Vector2(75) };
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Add(new SkipButton(Clock.CurrentTime + 5000));
|
||||
Add(new SkipOverlay(Clock.CurrentTime + 5000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,6 +130,7 @@
|
||||
<Compile Include="Visual\TestCaseEditorComposeRadioButtons.cs" />
|
||||
<Compile Include="Visual\TestCaseEditorComposeTimeline.cs" />
|
||||
<Compile Include="Visual\TestCaseEditorMenuBar.cs" />
|
||||
<Compile Include="Visual\TestCaseEditorSeekSnapping.cs" />
|
||||
<Compile Include="Visual\TestCaseEditorSummaryTimeline.cs" />
|
||||
<Compile Include="Visual\TestCaseEditorSelectionLayer.cs" />
|
||||
<Compile Include="Visual\TestCaseGamefield.cs" />
|
||||
|
@ -9,6 +9,7 @@ using System.Linq;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.IO.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.IO.Serialization.Converters;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
@ -16,14 +17,14 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// A Beatmap containing converted HitObjects.
|
||||
/// </summary>
|
||||
public class Beatmap<T> : IJsonSerializable
|
||||
public class Beatmap<T> : IJsonSerializable, IHasComboColours
|
||||
where T : HitObject
|
||||
{
|
||||
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
|
||||
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
|
||||
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
|
||||
|
||||
public List<Color4> ComboColors = new List<Color4>
|
||||
public List<Color4> ComboColours { get; set; } = new List<Color4>
|
||||
{
|
||||
new Color4(17, 136, 170, 255),
|
||||
new Color4(102, 136, 0, 255),
|
||||
@ -55,7 +56,7 @@ namespace osu.Game.Beatmaps
|
||||
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
|
||||
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
|
||||
Breaks = original?.Breaks ?? Breaks;
|
||||
ComboColors = original?.ComboColors ?? ComboColors;
|
||||
ComboColours = original?.ComboColours ?? ComboColours;
|
||||
HitObjects = original?.HitObjects ?? HitObjects;
|
||||
|
||||
if (original == null && Metadata == null)
|
||||
|
@ -50,9 +50,14 @@ namespace osu.Game.Beatmaps
|
||||
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original)
|
||||
{
|
||||
var beatmap = CreateBeatmap();
|
||||
|
||||
// todo: this *must* share logic (or directly use) Beatmap<T>'s constructor.
|
||||
// right now this isn't easily possible due to generic entanglement.
|
||||
beatmap.BeatmapInfo = original.BeatmapInfo;
|
||||
beatmap.ControlPointInfo = original.ControlPointInfo;
|
||||
beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList();
|
||||
beatmap.Breaks = original.Breaks;
|
||||
beatmap.ComboColours = original.ComboColours;
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
13
osu.Game/Beatmaps/Formats/IHasComboColours.cs
Normal file
13
osu.Game/Beatmaps/Formats/IHasComboColours.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// 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.Collections.Generic;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
public interface IHasComboColours
|
||||
{
|
||||
List<Color4> ComboColours { get; set; }
|
||||
}
|
||||
}
|
13
osu.Game/Beatmaps/Formats/IHasCustomColours.cs
Normal file
13
osu.Game/Beatmaps/Formats/IHasCustomColours.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// 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.Collections.Generic;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
public interface IHasCustomColours
|
||||
{
|
||||
Dictionary<string, Color4> CustomColours { get; set; }
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Objects.Legacy;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -19,7 +18,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private Beatmap beatmap;
|
||||
|
||||
private bool hasCustomColours;
|
||||
private ConvertHitObjectParser parser;
|
||||
|
||||
private LegacySampleBank defaultSampleBank;
|
||||
@ -72,29 +70,28 @@ namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
case Section.General:
|
||||
handleGeneral(line);
|
||||
break;
|
||||
return;
|
||||
case Section.Editor:
|
||||
handleEditor(line);
|
||||
break;
|
||||
return;
|
||||
case Section.Metadata:
|
||||
handleMetadata(line);
|
||||
break;
|
||||
return;
|
||||
case Section.Difficulty:
|
||||
handleDifficulty(line);
|
||||
break;
|
||||
return;
|
||||
case Section.Events:
|
||||
handleEvents(line);
|
||||
break;
|
||||
return;
|
||||
case Section.TimingPoints:
|
||||
handleTimingPoints(line);
|
||||
break;
|
||||
case Section.Colours:
|
||||
handleColours(line);
|
||||
break;
|
||||
return;
|
||||
case Section.HitObjects:
|
||||
handleHitObjects(line);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
base.ParseLine(beatmap, section, line);
|
||||
}
|
||||
|
||||
private void handleGeneral(string line)
|
||||
@ -364,38 +361,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
private void handleColours(string line)
|
||||
{
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
|
||||
string[] split = pair.Value.Split(',');
|
||||
|
||||
if (split.Length != 3)
|
||||
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
|
||||
|
||||
byte r, g, b;
|
||||
if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b))
|
||||
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
|
||||
|
||||
if (!hasCustomColours)
|
||||
{
|
||||
beatmap.ComboColors.Clear();
|
||||
hasCustomColours = true;
|
||||
}
|
||||
|
||||
// Note: the combo index specified in the beatmap is discarded
|
||||
if (pair.Key.StartsWith(@"Combo"))
|
||||
{
|
||||
beatmap.ComboColors.Add(new Color4
|
||||
{
|
||||
R = r / 255f,
|
||||
G = g / 255f,
|
||||
B = b / 255f,
|
||||
A = 1f,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void handleHitObjects(string line)
|
||||
{
|
||||
// If the ruleset wasn't specified, assume the osu!standard ruleset.
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
@ -40,7 +41,53 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//");
|
||||
|
||||
protected abstract void ParseLine(T output, Section section, string line);
|
||||
protected virtual void ParseLine(T output, Section section, string line)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case Section.Colours:
|
||||
handleColours(output, line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private bool hasComboColours;
|
||||
|
||||
private void handleColours(T output, string line)
|
||||
{
|
||||
var pair = SplitKeyVal(line, ':');
|
||||
|
||||
bool isCombo = pair.Key.StartsWith(@"Combo");
|
||||
|
||||
string[] split = pair.Value.Split(',');
|
||||
|
||||
if (split.Length != 3)
|
||||
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
|
||||
|
||||
if (!byte.TryParse(split[0], out var r) || !byte.TryParse(split[1], out var g) || !byte.TryParse(split[2], out var b))
|
||||
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
|
||||
|
||||
Color4 colour = new Color4(r, g, b, 255);
|
||||
|
||||
if (isCombo)
|
||||
{
|
||||
if (!(output is IHasComboColours tHasComboColours)) return;
|
||||
|
||||
if (!hasComboColours)
|
||||
{
|
||||
// remove default colours.
|
||||
tHasComboColours.ComboColours.Clear();
|
||||
hasComboColours = true;
|
||||
}
|
||||
|
||||
tHasComboColours.ComboColours.Add(colour);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(output is IHasCustomColours tHasCustomColours)) return;
|
||||
tHasCustomColours.CustomColours[pair.Key] = colour;
|
||||
}
|
||||
}
|
||||
|
||||
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator)
|
||||
{
|
||||
@ -65,6 +112,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
Colours,
|
||||
HitObjects,
|
||||
Variables,
|
||||
Fonts
|
||||
}
|
||||
|
||||
internal enum LegacySampleBank
|
||||
|
@ -46,11 +46,13 @@ namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
case Section.Events:
|
||||
handleEvents(line);
|
||||
break;
|
||||
return;
|
||||
case Section.Variables:
|
||||
handleVariables(line);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
base.ParseLine(storyboard, section, line);
|
||||
}
|
||||
|
||||
private void handleEvents(string line)
|
||||
|
@ -8,15 +8,17 @@ using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
public class APIAccess : IAPIProvider
|
||||
public class APIAccess : Component, IAPIProvider
|
||||
{
|
||||
private readonly OsuConfigManager config;
|
||||
private readonly OAuth authentication;
|
||||
|
||||
public string Endpoint = @"https://osu.ppy.sh";
|
||||
@ -25,13 +27,12 @@ namespace osu.Game.Online.API
|
||||
|
||||
private ConcurrentQueue<APIRequest> queue = new ConcurrentQueue<APIRequest>();
|
||||
|
||||
public Scheduler Scheduler = new Scheduler();
|
||||
/// <summary>
|
||||
/// The username/email provided by the user when initiating a login.
|
||||
/// </summary>
|
||||
public string ProvidedUsername { get; private set; }
|
||||
|
||||
public string Username;
|
||||
|
||||
//private SecurePassword password;
|
||||
|
||||
public string Password;
|
||||
private string password;
|
||||
|
||||
public Bindable<User> LocalUser { get; } = new Bindable<User>(createGuestUser());
|
||||
|
||||
@ -41,24 +42,31 @@ namespace osu.Game.Online.API
|
||||
set { authentication.Token = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); }
|
||||
}
|
||||
|
||||
protected bool HasLogin => Token != null || !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password);
|
||||
protected bool HasLogin => Token != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password);
|
||||
|
||||
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable (should dispose of this or at very least keep a reference).
|
||||
private readonly Thread thread;
|
||||
|
||||
private readonly Logger log;
|
||||
|
||||
public APIAccess()
|
||||
public APIAccess(OsuConfigManager config)
|
||||
{
|
||||
this.config = config;
|
||||
|
||||
authentication = new OAuth(client_id, client_secret, Endpoint);
|
||||
log = Logger.GetLogger(LoggingTarget.Network);
|
||||
|
||||
ProvidedUsername = config.Get<string>(OsuSetting.Username);
|
||||
Token = config.Get<string>(OsuSetting.Token);
|
||||
|
||||
thread = new Thread(run) { IsBackground = true };
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
private readonly List<IOnlineComponent> components = new List<IOnlineComponent>();
|
||||
|
||||
internal void Schedule(Action action) => base.Schedule(action);
|
||||
|
||||
public void Register(IOnlineComponent component)
|
||||
{
|
||||
Scheduler.Add(delegate
|
||||
@ -111,12 +119,15 @@ namespace osu.Game.Online.API
|
||||
|
||||
State = APIState.Connecting;
|
||||
|
||||
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, Password))
|
||||
// save the username at this point, if the user requested for it to be.
|
||||
config.Set(OsuSetting.Username, config.Get<bool>(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty);
|
||||
|
||||
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(ProvidedUsername, password))
|
||||
{
|
||||
//todo: this fails even on network-related issues. we should probably handle those differently.
|
||||
//NotificationOverlay.ShowMessage("Login failed!");
|
||||
log.Add(@"Login failed!");
|
||||
Password = null;
|
||||
password = null;
|
||||
authentication.Clear();
|
||||
continue;
|
||||
}
|
||||
@ -173,8 +184,8 @@ namespace osu.Game.Online.API
|
||||
{
|
||||
Debug.Assert(State == APIState.Offline);
|
||||
|
||||
Username = username;
|
||||
Password = password;
|
||||
ProvidedUsername = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -283,8 +294,8 @@ namespace osu.Game.Online.API
|
||||
public void Logout(bool clearUsername = true)
|
||||
{
|
||||
flushQueue();
|
||||
if (clearUsername) Username = null;
|
||||
Password = null;
|
||||
if (clearUsername) ProvidedUsername = null;
|
||||
password = null;
|
||||
authentication.Clear();
|
||||
LocalUser.Value = createGuestUser();
|
||||
}
|
||||
@ -295,9 +306,12 @@ namespace osu.Game.Online.API
|
||||
Id = 1,
|
||||
};
|
||||
|
||||
public void Update()
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
Scheduler.Update();
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
config.Set(OsuSetting.Token, config.Get<bool>(OsuSetting.SavePassword) ? Token : string.Empty);
|
||||
config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Online.API
|
||||
return request;
|
||||
}
|
||||
|
||||
private void request_Progress(long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); });
|
||||
private void request_Progress(long current, long total) => API.Schedule(() => Progress?.Invoke(current, total));
|
||||
|
||||
protected APIDownloadRequest()
|
||||
{
|
||||
|
@ -85,7 +85,7 @@ namespace osu.Game.Online.API
|
||||
if (checkAndProcessFailure())
|
||||
return;
|
||||
|
||||
api.Scheduler.Add(delegate { Success?.Invoke(); });
|
||||
api.Schedule(delegate { Success?.Invoke(); });
|
||||
}
|
||||
|
||||
public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled"));
|
||||
@ -108,7 +108,7 @@ namespace osu.Game.Online.API
|
||||
{
|
||||
if (API == null || pendingFailure == null) return cancelled;
|
||||
|
||||
API.Scheduler.Add(pendingFailure);
|
||||
API.Schedule(pendingFailure);
|
||||
pendingFailure = null;
|
||||
return true;
|
||||
}
|
||||
|
@ -1,13 +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 osu.Framework;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
public interface IAPIProvider : IUpdateable
|
||||
public interface IAPIProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The local user.
|
||||
|
@ -34,7 +34,7 @@ using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game
|
||||
{
|
||||
public class OsuGameBase : Framework.Game, IOnlineComponent, ICanAcceptFiles
|
||||
public class OsuGameBase : Framework.Game, ICanAcceptFiles
|
||||
{
|
||||
protected OsuConfigManager LocalConfig;
|
||||
|
||||
@ -56,8 +56,6 @@ namespace osu.Game
|
||||
|
||||
protected override string MainResourceFile => @"osu.Game.Resources.dll";
|
||||
|
||||
public APIAccess API;
|
||||
|
||||
private Container content;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
@ -108,16 +106,14 @@ namespace osu.Game
|
||||
|
||||
dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio));
|
||||
|
||||
dependencies.Cache(API = new APIAccess
|
||||
{
|
||||
Username = LocalConfig.Get<string>(OsuSetting.Username),
|
||||
Token = LocalConfig.Get<string>(OsuSetting.Token)
|
||||
});
|
||||
dependencies.CacheAs<IAPIProvider>(API);
|
||||
var api = new APIAccess(LocalConfig);
|
||||
|
||||
dependencies.Cache(api);
|
||||
dependencies.CacheAs<IAPIProvider>(api);
|
||||
|
||||
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
|
||||
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
|
||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, API, Host));
|
||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Host));
|
||||
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore));
|
||||
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
|
||||
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
|
||||
@ -183,9 +179,9 @@ namespace osu.Game
|
||||
lastBeatmap = b;
|
||||
};
|
||||
|
||||
API.Register(this);
|
||||
|
||||
FileStore.Cleanup();
|
||||
|
||||
AddInternal(api);
|
||||
}
|
||||
|
||||
private void runMigrations()
|
||||
@ -211,16 +207,6 @@ namespace osu.Game
|
||||
|
||||
private WorkingBeatmap lastBeatmap;
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case APIState.Online:
|
||||
LocalConfig.Set(OsuSetting.Username, LocalConfig.Get<bool>(OsuSetting.SaveUsername) ? API.Username : string.Empty);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -253,24 +239,6 @@ namespace osu.Game
|
||||
base.SetHost(host);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
API.Update();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
//refresh token may have changed.
|
||||
if (LocalConfig != null && API != null)
|
||||
{
|
||||
LocalConfig.Set(OsuSetting.Token, LocalConfig.Get<bool>(OsuSetting.SavePassword) ? API.Token : string.Empty);
|
||||
LocalConfig.Save();
|
||||
}
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
private readonly List<ICanAcceptFiles> fileImporters = new List<ICanAcceptFiles>();
|
||||
|
||||
public void Import(params string[] paths)
|
||||
|
@ -186,7 +186,7 @@ namespace osu.Game.Overlays.Direct
|
||||
progressBar.FadeOut(500);
|
||||
};
|
||||
|
||||
request.DownloadProgressed += progress => progressBar.Current.Value = progress;
|
||||
request.DownloadProgressed += progress => Schedule(() => progressBar.Current.Value = progress);
|
||||
|
||||
request.Success += data =>
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public DifficultyIncreaseSection()
|
||||
{
|
||||
Header = @"Gameplay Difficulty Increase";
|
||||
Header = @"Difficulty Increase";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public DifficultyReductionSection()
|
||||
{
|
||||
Header = @"Gameplay Difficulty Reduction";
|
||||
Header = @"Difficulty Reduction";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,14 +27,8 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public string Header
|
||||
{
|
||||
get
|
||||
{
|
||||
return headerLabel.Text;
|
||||
}
|
||||
set
|
||||
{
|
||||
headerLabel.Text = value;
|
||||
}
|
||||
get => headerLabel.Text;
|
||||
set => headerLabel.Text = value;
|
||||
}
|
||||
|
||||
public IEnumerable<Mod> SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null);
|
||||
@ -47,12 +41,12 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
if (m == null)
|
||||
return new ModButtonEmpty();
|
||||
else
|
||||
return new ModButton(m)
|
||||
{
|
||||
SelectedColour = selectedColour,
|
||||
SelectionChanged = Action,
|
||||
};
|
||||
|
||||
return new ModButton(m)
|
||||
{
|
||||
SelectedColour = selectedColour,
|
||||
SelectionChanged = Action,
|
||||
};
|
||||
}).ToArray();
|
||||
|
||||
ButtonsContainer.Children = modContainers;
|
||||
@ -65,10 +59,7 @@ namespace osu.Game.Overlays.Mods
|
||||
private Color4 selectedColour = Color4.White;
|
||||
public Color4 SelectedColour
|
||||
{
|
||||
get
|
||||
{
|
||||
return selectedColour;
|
||||
}
|
||||
get => selectedColour;
|
||||
set
|
||||
{
|
||||
if (value == selectedColour) return;
|
||||
@ -102,31 +93,31 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
Mod selected = button.SelectedMod;
|
||||
if (selected == null) continue;
|
||||
foreach (Type type in modTypes)
|
||||
foreach (var type in modTypes)
|
||||
if (type.IsInstanceOfType(selected))
|
||||
{
|
||||
if (immediate)
|
||||
button.Deselect();
|
||||
else
|
||||
Scheduler.AddDelayed(() => button.Deselect(), delay += 50);
|
||||
Scheduler.AddDelayed(button.Deselect, delay += 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select one or more mods in this section.
|
||||
/// Select one or more mods in this section and deselects all other ones.
|
||||
/// </summary>
|
||||
/// <param name="mods">The types of <see cref="Mod"/>s which should be deselected.</param>
|
||||
public void SelectTypes(IEnumerable<Mod> mods)
|
||||
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be selected.</param>
|
||||
public void SelectTypes(IEnumerable<Type> modTypes)
|
||||
{
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
for (int i = 0; i < button.Mods.Length; i++)
|
||||
{
|
||||
foreach (var mod in mods)
|
||||
if (mod.GetType().IsInstanceOfType(button.Mods[i]))
|
||||
button.SelectAt(i);
|
||||
}
|
||||
int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m)));
|
||||
|
||||
if (i >= 0)
|
||||
button.SelectAt(i);
|
||||
else
|
||||
button.Deselect();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Mods
|
||||
private void selectedModsChanged(IEnumerable<Mod> obj)
|
||||
{
|
||||
foreach (ModSection section in ModSectionsContainer.Children)
|
||||
section.SelectTypes(obj);
|
||||
section.SelectTypes(obj.Select(m => m.GetType()).ToList());
|
||||
|
||||
updateMods();
|
||||
}
|
||||
@ -287,7 +287,7 @@ namespace osu.Game.Overlays.Mods
|
||||
Anchor = Anchor.TopCentre,
|
||||
Action = modButtonPressed,
|
||||
},
|
||||
new AssistedSection
|
||||
new SpecialSection
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Origin = Anchor.TopCentre,
|
||||
|
@ -8,7 +8,7 @@ using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
public class AssistedSection : ModSection
|
||||
public class SpecialSection : ModSection
|
||||
{
|
||||
protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M };
|
||||
public override ModType ModType => ModType.Special;
|
||||
@ -19,9 +19,9 @@ namespace osu.Game.Overlays.Mods
|
||||
SelectedColour = colours.BlueLight;
|
||||
}
|
||||
|
||||
public AssistedSection()
|
||||
public SpecialSection()
|
||||
{
|
||||
Header = @"Assisted";
|
||||
Header = @"Special";
|
||||
}
|
||||
}
|
||||
}
|
@ -210,7 +210,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
{
|
||||
PlaceholderText = "Email address",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = api?.Username ?? string.Empty,
|
||||
Text = api?.ProvidedUsername ?? string.Empty,
|
||||
TabbableContentContainer = this
|
||||
},
|
||||
password = new OsuPasswordTextBox
|
||||
|
@ -5,9 +5,12 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
@ -27,21 +30,28 @@ namespace osu.Game.Rulesets.Edit
|
||||
private RulesetContainer rulesetContainer;
|
||||
private readonly List<Container> layerContainers = new List<Container>();
|
||||
|
||||
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
private IAdjustableClock adjustableClock;
|
||||
|
||||
protected HitObjectComposer(Ruleset ruleset)
|
||||
{
|
||||
this.ruleset = ruleset;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osuGame)
|
||||
private void load(OsuGameBase osuGame, IAdjustableClock adjustableClock, IFrameBasedClock framedClock)
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
|
||||
beatmap.BindTo(osuGame.Beatmap);
|
||||
|
||||
try
|
||||
{
|
||||
rulesetContainer = CreateRulesetContainer(ruleset, osuGame.Beatmap.Value);
|
||||
|
||||
// TODO: should probably be done at a RulesetContainer level to share logic with Player.
|
||||
rulesetContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)osuGame.Beatmap.Value.Track ?? new StopwatchClock());
|
||||
rulesetContainer = CreateRulesetContainer(ruleset, beatmap.Value);
|
||||
rulesetContainer.Clock = framedClock;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -134,6 +144,112 @@ namespace osu.Game.Rulesets.Edit
|
||||
});
|
||||
}
|
||||
|
||||
protected override bool OnWheel(InputState state)
|
||||
{
|
||||
if (state.Mouse.WheelDelta > 0)
|
||||
SeekBackward(true);
|
||||
else
|
||||
SeekForward(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeks the current time one beat-snapped beat-length backwards.
|
||||
/// </summary>
|
||||
/// <param name="snapped">Whether to snap to the closest beat.</param>
|
||||
public void SeekBackward(bool snapped = false) => seek(-1, snapped);
|
||||
|
||||
/// <summary>
|
||||
/// Seeks the current time one beat-snapped beat-length forwards.
|
||||
/// </summary>
|
||||
/// <param name="snapped">Whether to snap to the closest beat.</param>
|
||||
public void SeekForward(bool snapped = false) => seek(1, snapped);
|
||||
|
||||
private void seek(int direction, bool snapped)
|
||||
{
|
||||
// Todo: This should not be a constant, but feels good for now
|
||||
const int beat_snap_divisor = 4;
|
||||
|
||||
var cpi = beatmap.Value.Beatmap.ControlPointInfo;
|
||||
|
||||
var timingPoint = cpi.TimingPointAt(adjustableClock.CurrentTime);
|
||||
if (direction < 0 && timingPoint.Time == adjustableClock.CurrentTime)
|
||||
{
|
||||
// When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into
|
||||
int activeIndex = cpi.TimingPoints.IndexOf(timingPoint);
|
||||
while (activeIndex > 0 && adjustableClock.CurrentTime == timingPoint.Time)
|
||||
timingPoint = cpi.TimingPoints[--activeIndex];
|
||||
}
|
||||
|
||||
double seekAmount = timingPoint.BeatLength / beat_snap_divisor;
|
||||
double seekTime = adjustableClock.CurrentTime + seekAmount * direction;
|
||||
|
||||
if (!snapped || cpi.TimingPoints.Count == 0)
|
||||
{
|
||||
adjustableClock.Seek(seekTime);
|
||||
return;
|
||||
}
|
||||
|
||||
// We will be snapping to beats within timingPoint
|
||||
seekTime -= timingPoint.Time;
|
||||
|
||||
// Determine the index from timingPoint of the closest beat to seekTime, accounting for scrolling direction
|
||||
int closestBeat;
|
||||
if (direction > 0)
|
||||
closestBeat = (int)Math.Floor(seekTime / seekAmount);
|
||||
else
|
||||
closestBeat = (int)Math.Ceiling(seekTime / seekAmount);
|
||||
|
||||
seekTime = timingPoint.Time + closestBeat * seekAmount;
|
||||
|
||||
// Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this.
|
||||
// Instead, we'll go to the next beat in the direction when this is the case
|
||||
if (Precision.AlmostEquals(adjustableClock.CurrentTime, seekTime))
|
||||
{
|
||||
closestBeat += direction > 0 ? 1 : -1;
|
||||
seekTime = timingPoint.Time + closestBeat * seekAmount;
|
||||
}
|
||||
|
||||
if (seekTime < timingPoint.Time && timingPoint != cpi.TimingPoints.First())
|
||||
seekTime = timingPoint.Time;
|
||||
|
||||
var nextTimingPoint = cpi.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time);
|
||||
if (seekTime > nextTimingPoint?.Time)
|
||||
seekTime = nextTimingPoint.Time;
|
||||
|
||||
adjustableClock.Seek(seekTime);
|
||||
}
|
||||
|
||||
public void SeekTo(double seekTime, bool snapped = false)
|
||||
{
|
||||
// Todo: This should not be a constant, but feels good for now
|
||||
const int beat_snap_divisor = 4;
|
||||
|
||||
if (!snapped)
|
||||
{
|
||||
adjustableClock.Seek(seekTime);
|
||||
return;
|
||||
}
|
||||
|
||||
var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime);
|
||||
double beatSnapLength = timingPoint.BeatLength / beat_snap_divisor;
|
||||
|
||||
// We will be snapping to beats within the timing point
|
||||
seekTime -= timingPoint.Time;
|
||||
|
||||
// Determine the index from the current timing point of the closest beat to seekTime
|
||||
int closestBeat = (int)Math.Round(seekTime / beatSnapLength);
|
||||
seekTime = timingPoint.Time + closestBeat * beatSnapLength;
|
||||
|
||||
// Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to
|
||||
// the next timing point's start time
|
||||
var nextTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time);
|
||||
if (seekTime > nextTimingPoint?.Time)
|
||||
seekTime = nextTimingPoint.Time;
|
||||
|
||||
adjustableClock.Seek(seekTime);
|
||||
}
|
||||
|
||||
private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool;
|
||||
|
||||
protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true);
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Name => "Autoplay";
|
||||
public override string ShortenedName => "AT";
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto;
|
||||
public override string Description => "Watch a perfect automated play through the song";
|
||||
public override string Description => "Watch a perfect automated play through the song.";
|
||||
public override double ScoreMultiplier => 0;
|
||||
public bool AllowFail => false;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
|
||||
|
@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string ShortenedName => "CN";
|
||||
public override bool HasImplementation => false;
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema;
|
||||
public override string Description => "Watch the video without visual distractions.";
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Name => "Daycore";
|
||||
public override string ShortenedName => "DC";
|
||||
public override FontAwesome Icon => FontAwesome.fa_question;
|
||||
public override string Description => "whoaaaaa";
|
||||
public override string Description => "Whoaaaaa...";
|
||||
|
||||
public override void ApplyToClock(IAdjustableClock clock)
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user