diff --git a/osu.Android.props b/osu.Android.props
index 7e17f9da16..b147fdd05b 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index b9d791fdb1..212365caad 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch
new KeyBinding(InputKey.Shift, CatchAction.Dash),
};
- public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ public override IEnumerable ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new CatchModNightcore();
diff --git a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs
index b41a5e0612..9dab3ed630 100644
--- a/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs
+++ b/osu.Game.Rulesets.Catch/Replays/CatchReplayFrame.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Catch.Replays
}
}
- public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
+ public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
Position = currentFrame.Position.X / CatchPlayfield.BASE_WIDTH;
Dashing = currentFrame.ButtonState == ReplayButtonState.Left1;
@@ -56,5 +56,14 @@ namespace osu.Game.Rulesets.Catch.Replays
Actions.Add(CatchAction.MoveLeft);
}
}
+
+ public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
+ {
+ ReplayButtonState state = ReplayButtonState.None;
+
+ if (Actions.Contains(CatchAction.Dash)) state |= ReplayButtonState.Left1;
+
+ return new LegacyReplayFrame(Time, Position * CatchPlayfield.BASE_WIDTH, null, state);
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs b/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs
new file mode 100644
index 0000000000..9a4d1f9585
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs
@@ -0,0 +1,26 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Game.Replays;
+using osu.Game.Rulesets.Catch.Replays;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.UI
+{
+ public class CatchReplayRecorder : ReplayRecorder
+ {
+ private readonly CatchPlayfield playfield;
+
+ public CatchReplayRecorder(Replay target, CatchPlayfield playfield)
+ : base(target)
+ {
+ this.playfield = playfield;
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame)
+ => new CatchReplayFrame(Time.Current, playfield.CatcherArea.MovableCatcher.X, actions.Contains(CatchAction.Dash), previousFrame as CatchReplayFrame);
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs
index e361b29a9d..8fa9c61b6f 100644
--- a/osu.Game.Rulesets.Catch/UI/Catcher.cs
+++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs
@@ -141,14 +141,14 @@ namespace osu.Game.Rulesets.Catch.UI
var ourRadius = fruit.DisplayRadius;
float theirRadius = 0;
- const float allowance = 6;
+ const float allowance = 10;
while (caughtFruit.Any(f =>
f.LifetimeEnd == double.MaxValue &&
Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2)))
{
var diff = (ourRadius + theirRadius) / allowance;
- fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff;
+ fruit.X += (RNG.NextSingle() - 0.5f) * diff * 2;
fruit.Y -= RNG.NextSingle() * diff;
}
diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
index fd8a1d175d..ebe45aa3ab 100644
--- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs
@@ -32,6 +32,8 @@ namespace osu.Game.Rulesets.Catch.UI
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
+ protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new CatchReplayRecorder(replay, (CatchPlayfield)Playfield);
+
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation);
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchPlayfieldAdjustmentContainer();
diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs b/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs
index 909d0d45c6..9049bb3a82 100644
--- a/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs
+++ b/osu.Game.Rulesets.Mania.Tests/ManiaInputTestScene.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
}
- protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ protected override KeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new LocalKeyBindingContainer(ruleset, variant, unique);
private class LocalKeyBindingContainer : RulesetKeyBindingContainer
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index b7b523a94d..9d06bd7c25 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new ManiaLegacySkinTransformer(source);
- public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ public override IEnumerable ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new ManiaModNightcore();
@@ -118,6 +118,59 @@ namespace osu.Game.Rulesets.Mania
yield return new ManiaModRandom();
}
+ public override LegacyMods ConvertToLegacyMods(Mod[] mods)
+ {
+ var value = base.ConvertToLegacyMods(mods);
+
+ foreach (var mod in mods)
+ {
+ switch (mod)
+ {
+ case ManiaModKey1 _:
+ value |= LegacyMods.Key1;
+ break;
+
+ case ManiaModKey2 _:
+ value |= LegacyMods.Key2;
+ break;
+
+ case ManiaModKey3 _:
+ value |= LegacyMods.Key3;
+ break;
+
+ case ManiaModKey4 _:
+ value |= LegacyMods.Key4;
+ break;
+
+ case ManiaModKey5 _:
+ value |= LegacyMods.Key5;
+ break;
+
+ case ManiaModKey6 _:
+ value |= LegacyMods.Key6;
+ break;
+
+ case ManiaModKey7 _:
+ value |= LegacyMods.Key7;
+ break;
+
+ case ManiaModKey8 _:
+ value |= LegacyMods.Key8;
+ break;
+
+ case ManiaModKey9 _:
+ value |= LegacyMods.Key9;
+ break;
+
+ case ManiaModFadeIn _:
+ value |= LegacyMods.FadeIn;
+ break;
+ }
+ }
+
+ return value;
+ }
+
public override IEnumerable GetModsFor(ModType type)
{
switch (type)
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
index 14b36fb765..699c58c373 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs
@@ -3,24 +3,17 @@
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
-using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
-using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
- public class ManiaModRandom : Mod, IApplicableToBeatmap
+ public class ManiaModRandom : ModRandom, IApplicableToBeatmap
{
- public override string Name => "Random";
- public override string Acronym => "RD";
- public override ModType Type => ModType.Conversion;
- public override IconUsage? Icon => OsuIcon.Dice;
public override string Description => @"Shuffle around the keys!";
- public override double ScoreMultiplier => 1;
public void ApplyToBeatmap(IBeatmap beatmap)
{
diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
index 877a9ee410..8c73c36e99 100644
--- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
+++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
+using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Replays.Legacy;
using osu.Game.Rulesets.Mania.Beatmaps;
@@ -24,15 +25,9 @@ namespace osu.Game.Rulesets.Mania.Replays
Actions.AddRange(actions);
}
- public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
+ public void FromLegacy(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
- // We don't need to fully convert, just create the converter
- var converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
-
- // NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling
- // elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage.
-
- var stage = new StageDefinition { Columns = converter.TargetColumns };
+ var maniaBeatmap = (ManiaBeatmap)beatmap;
var normalAction = ManiaAction.Key1;
var specialAction = ManiaAction.Special1;
@@ -42,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Replays
while (activeColumns > 0)
{
- var isSpecial = stage.IsSpecialColumn(counter);
+ var isSpecial = maniaBeatmap.Stages.First().IsSpecialColumn(counter);
if ((activeColumns & 1) > 0)
Actions.Add(isSpecial ? specialAction : normalAction);
@@ -56,5 +51,40 @@ namespace osu.Game.Rulesets.Mania.Replays
activeColumns >>= 1;
}
}
+
+ public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
+ {
+ var maniaBeatmap = (ManiaBeatmap)beatmap;
+
+ int keys = 0;
+
+ var specialColumns = new List();
+
+ for (int i = 0; i < maniaBeatmap.TotalColumns; i++)
+ {
+ if (maniaBeatmap.Stages.First().IsSpecialColumn(i))
+ specialColumns.Add(i);
+ }
+
+ foreach (var action in Actions)
+ {
+ switch (action)
+ {
+ case ManiaAction.Special1:
+ keys |= 1 << specialColumns[0];
+ break;
+
+ case ManiaAction.Special2:
+ keys |= 1 << specialColumns[1];
+ break;
+
+ default:
+ keys |= 1 << (action - ManiaAction.Key1);
+ break;
+ }
+ }
+
+ return new LegacyReplayFrame(Time, keys, null, ReplayButtonState.None);
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
index 2c497541a8..e5ec054fa7 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs
@@ -85,5 +85,7 @@ namespace osu.Game.Rulesets.Mania.UI
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
+
+ protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new ManiaReplayRecorder(replay);
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs b/osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs
new file mode 100644
index 0000000000..18275000a2
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs
@@ -0,0 +1,23 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Game.Replays;
+using osu.Game.Rulesets.Mania.Replays;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.UI
+{
+ public class ManiaReplayRecorder : ReplayRecorder
+ {
+ public ManiaReplayRecorder(Replay replay)
+ : base(replay)
+ {
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame)
+ => new ManiaReplayFrame(Time.Current, actions.ToArray());
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 2d5b9d874c..5c7f4a42b3 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -11,6 +11,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Skinning;
+using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
using osu.Game.Skinning;
@@ -196,6 +197,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ApplyResult(r => r.Type = r.Judgement.MaxResult);
}
+ public override void PlaySamples()
+ {
+ // rather than doing it this way, we should probably attach the sample to the tail circle.
+ // this can only be done after we stop using LegacyLastTick.
+ if (TailCircle.Result.Type != HitResult.Miss)
+ base.PlaySamples();
+ }
+
protected override void UpdateStateTransforms(ArmedState state)
{
base.UpdateStateTransforms(state);
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs
index a8fd3764c5..ac6c6905e4 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderRepeat.cs
@@ -34,8 +34,6 @@ namespace osu.Game.Rulesets.Osu.Objects
public class SliderRepeatJudgement : OsuJudgement
{
- public override bool IsBonus => true;
-
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 30 : 0;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
index 212a84c04a..22f3f559db 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
@@ -36,8 +36,6 @@ namespace osu.Game.Rulesets.Osu.Objects
public class SliderTickJudgement : OsuJudgement
{
- public override bool IsBonus => true;
-
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 10 : 0;
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs
index cdea7276f3..c8fe4f41ca 100644
--- a/osu.Game.Rulesets.Osu/OsuInputManager.cs
+++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu
///
public bool AllowUserCursorMovement { get; set; } = true;
- protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ protected override KeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new OsuKeyBindingContainer(ruleset, variant, unique);
public OsuInputManager(RulesetInfo ruleset)
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index ed73a54815..a0f5b8fe01 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
};
- public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ public override IEnumerable ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new OsuModNightcore();
diff --git a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs
index e6c6db5e61..3db81d70da 100644
--- a/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs
+++ b/osu.Game.Rulesets.Osu/Replays/OsuReplayFrame.cs
@@ -26,11 +26,23 @@ namespace osu.Game.Rulesets.Osu.Replays
Actions.AddRange(actions);
}
- public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
+ public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
Position = currentFrame.Position;
if (currentFrame.MouseLeft) Actions.Add(OsuAction.LeftButton);
if (currentFrame.MouseRight) Actions.Add(OsuAction.RightButton);
}
+
+ public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
+ {
+ ReplayButtonState state = ReplayButtonState.None;
+
+ if (Actions.Contains(OsuAction.LeftButton))
+ state |= ReplayButtonState.Left1;
+ if (Actions.Contains(OsuAction.RightButton))
+ state |= ReplayButtonState.Right1;
+
+ return new LegacyReplayFrame(Time, Position.X, Position.Y, state);
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
index a37ef8d9a0..b4d51d11c9 100644
--- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
@@ -58,6 +58,8 @@ namespace osu.Game.Rulesets.Osu.UI
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuFramedReplayInputHandler(replay);
+ protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new OsuReplayRecorder(replay);
+
public override double GameplayStartTime
{
get
diff --git a/osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs b/osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs
new file mode 100644
index 0000000000..b68ea136d5
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs
@@ -0,0 +1,23 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Game.Replays;
+using osu.Game.Rulesets.Osu.Replays;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.UI
+{
+ public class OsuReplayRecorder : ReplayRecorder
+ {
+ public OsuReplayRecorder(Replay replay)
+ : base(replay)
+ {
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame)
+ => new OsuReplayFrame(Time.Current, mousePosition, actions.ToArray());
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs
new file mode 100644
index 0000000000..1cf19ac18e
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs
@@ -0,0 +1,27 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Utils;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Taiko.Beatmaps;
+using osu.Game.Rulesets.Taiko.Objects;
+
+namespace osu.Game.Rulesets.Taiko.Mods
+{
+ public class TaikoModRandom : ModRandom, IApplicableToBeatmap
+ {
+ public override string Description => @"Shuffle around the colours!";
+
+ public void ApplyToBeatmap(IBeatmap beatmap)
+ {
+ var taikoBeatmap = (TaikoBeatmap)beatmap;
+
+ foreach (var obj in taikoBeatmap.HitObjects)
+ {
+ if (obj is Hit hit)
+ hit.Type = RNG.Next(2) == 0 ? HitType.Centre : HitType.Rim;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
index c5ebefc397..d2a7329a28 100644
--- a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
+++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
@@ -23,12 +23,24 @@ namespace osu.Game.Rulesets.Taiko.Replays
Actions.AddRange(actions);
}
- public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
+ public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
if (currentFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim);
if (currentFrame.MouseRight2) Actions.Add(TaikoAction.RightRim);
if (currentFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre);
if (currentFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre);
}
+
+ public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
+ {
+ ReplayButtonState state = ReplayButtonState.None;
+
+ if (Actions.Contains(TaikoAction.LeftRim)) state |= ReplayButtonState.Right1;
+ if (Actions.Contains(TaikoAction.RightRim)) state |= ReplayButtonState.Right2;
+ if (Actions.Contains(TaikoAction.LeftCentre)) state |= ReplayButtonState.Left1;
+ if (Actions.Contains(TaikoAction.RightCentre)) state |= ReplayButtonState.Left2;
+
+ return new LegacyReplayFrame(Time, null, null, state);
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index fc79e59864..a6c9a33569 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Taiko
new KeyBinding(InputKey.K, TaikoAction.RightRim),
};
- public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ public override IEnumerable ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new TaikoModNightcore();
@@ -114,6 +114,7 @@ namespace osu.Game.Rulesets.Taiko
case ModType.Conversion:
return new Mod[]
{
+ new TaikoModRandom(),
new TaikoModDifficultyAdjust(),
};
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
index 9196bbf13e..e4a4b555a7 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs
@@ -65,5 +65,7 @@ namespace osu.Game.Rulesets.Taiko.UI
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay);
+
+ protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new TaikoReplayRecorder(replay);
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs b/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs
new file mode 100644
index 0000000000..1859dabf03
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs
@@ -0,0 +1,23 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Game.Replays;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.Taiko.Replays;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Rulesets.Taiko.UI
+{
+ public class TaikoReplayRecorder : ReplayRecorder
+ {
+ public TaikoReplayRecorder(Replay replay)
+ : base(replay)
+ {
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame) =>
+ new TaikoReplayFrame(Time.Current, actions.ToArray());
+ }
+}
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
index 76b76aa357..2fdeadca02 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
@@ -26,34 +26,34 @@ namespace osu.Game.Tests.Beatmaps.Formats
var storyboard = decoder.Decode(stream);
Assert.IsTrue(storyboard.HasDrawable);
- Assert.AreEqual(4, storyboard.Layers.Count());
+ Assert.AreEqual(5, storyboard.Layers.Count());
StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
Assert.IsNotNull(background);
Assert.AreEqual(16, background.Elements.Count);
- Assert.IsTrue(background.EnabledWhenFailing);
- Assert.IsTrue(background.EnabledWhenPassing);
+ Assert.IsTrue(background.VisibleWhenFailing);
+ Assert.IsTrue(background.VisibleWhenPassing);
Assert.AreEqual("Background", background.Name);
StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2);
Assert.IsNotNull(fail);
Assert.AreEqual(0, fail.Elements.Count);
- Assert.IsTrue(fail.EnabledWhenFailing);
- Assert.IsFalse(fail.EnabledWhenPassing);
+ Assert.IsTrue(fail.VisibleWhenFailing);
+ Assert.IsFalse(fail.VisibleWhenPassing);
Assert.AreEqual("Fail", fail.Name);
StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1);
Assert.IsNotNull(pass);
Assert.AreEqual(0, pass.Elements.Count);
- Assert.IsFalse(pass.EnabledWhenFailing);
- Assert.IsTrue(pass.EnabledWhenPassing);
+ Assert.IsFalse(pass.VisibleWhenFailing);
+ Assert.IsTrue(pass.VisibleWhenPassing);
Assert.AreEqual("Pass", pass.Name);
StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0);
Assert.IsNotNull(foreground);
Assert.AreEqual(151, foreground.Elements.Count);
- Assert.IsTrue(foreground.EnabledWhenFailing);
- Assert.IsTrue(foreground.EnabledWhenPassing);
+ Assert.IsTrue(foreground.VisibleWhenFailing);
+ Assert.IsTrue(foreground.VisibleWhenPassing);
Assert.AreEqual("Foreground", foreground.Name);
int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite));
diff --git a/osu.Game.Tests/Gameplay/TestSceneReplayRecorder.cs b/osu.Game.Tests/Gameplay/TestSceneReplayRecorder.cs
new file mode 100644
index 0000000000..734991b868
--- /dev/null
+++ b/osu.Game.Tests/Gameplay/TestSceneReplayRecorder.cs
@@ -0,0 +1,282 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Bindings;
+using osu.Framework.Input.Events;
+using osu.Framework.Input.StateChanges;
+using osu.Framework.Testing;
+using osu.Framework.Threading;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Replays;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.UI;
+using osu.Game.Tests.Visual;
+using osu.Game.Tests.Visual.UserInterface;
+using osuTK;
+using osuTK.Graphics;
+using osuTK.Input;
+
+namespace osu.Game.Tests.Gameplay
+{
+ public class TestSceneReplayRecorder : OsuManualInputManagerTestScene
+ {
+ private TestRulesetInputManager playbackManager;
+ private TestRulesetInputManager recordingManager;
+
+ private Replay replay;
+
+ private TestReplayRecorder recorder;
+
+ [SetUp]
+ public void SetUp() => Schedule(() =>
+ {
+ replay = new Replay();
+
+ Add(new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
+ {
+ Recorder = recorder = new TestReplayRecorder(replay)
+ {
+ ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
+ },
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = Color4.Brown,
+ RelativeSizeAxes = Axes.Both,
+ },
+ new OsuSpriteText
+ {
+ Text = "Recording",
+ Scale = new Vector2(3),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new TestInputConsumer()
+ }
+ },
+ }
+ },
+ new Drawable[]
+ {
+ playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
+ {
+ ReplayInputHandler = new TestFramedReplayInputHandler(replay)
+ {
+ GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
+ },
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = Color4.DarkBlue,
+ RelativeSizeAxes = Axes.Both,
+ },
+ new OsuSpriteText
+ {
+ Text = "Playback",
+ Scale = new Vector2(3),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new TestInputConsumer()
+ }
+ },
+ }
+ }
+ }
+ });
+ });
+
+ [Test]
+ public void TestBasic()
+ {
+ AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
+ AddUntilStep("one frame recorded", () => replay.Frames.Count == 1);
+ AddAssert("position matches", () => playbackManager.ChildrenOfType().First().Position == recordingManager.ChildrenOfType().First().Position);
+ }
+
+ [Test]
+ public void TestHighFrameRate()
+ {
+ ScheduledDelegate moveFunction = null;
+
+ AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
+ AddStep("much move", () => moveFunction = Scheduler.AddDelayed(() =>
+ InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0)), 10, true));
+ AddWaitStep("move", 10);
+ AddStep("stop move", () => moveFunction.Cancel());
+ AddAssert("at least 60 frames recorded", () => replay.Frames.Count > 60);
+ }
+
+ [Test]
+ public void TestLimitedFrameRate()
+ {
+ ScheduledDelegate moveFunction = null;
+
+ AddStep("lower rate", () => recorder.RecordFrameRate = 2);
+ AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
+ AddStep("much move", () => moveFunction = Scheduler.AddDelayed(() =>
+ InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0)), 10, true));
+ AddWaitStep("move", 10);
+ AddStep("stop move", () => moveFunction.Cancel());
+ AddAssert("less than 10 frames recorded", () => replay.Frames.Count < 10);
+ }
+
+ [Test]
+ public void TestLimitedFrameRateWithImportantFrames()
+ {
+ ScheduledDelegate moveFunction = null;
+
+ AddStep("lower rate", () => recorder.RecordFrameRate = 2);
+ AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
+ AddStep("much move with press", () => moveFunction = Scheduler.AddDelayed(() =>
+ {
+ InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0));
+ InputManager.PressButton(MouseButton.Left);
+ InputManager.ReleaseButton(MouseButton.Left);
+ }, 10, true));
+ AddWaitStep("move", 10);
+ AddStep("stop move", () => moveFunction.Cancel());
+ AddAssert("at least 60 frames recorded", () => replay.Frames.Count > 60);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+ playbackManager?.ReplayInputHandler.SetFrameFromTime(Time.Current - 100);
+ }
+
+ public class TestFramedReplayInputHandler : FramedReplayInputHandler
+ {
+ public TestFramedReplayInputHandler(Replay replay)
+ : base(replay)
+ {
+ }
+
+ public override List GetPendingInputs()
+ {
+ return new List
+ {
+ new MousePositionAbsoluteInput
+ {
+ Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero)
+ },
+ new ReplayState
+ {
+ PressedActions = CurrentFrame?.Actions ?? new List()
+ }
+ };
+ }
+ }
+
+ public class TestInputConsumer : CompositeDrawable, IKeyBindingHandler
+ {
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent.ReceivePositionalInputAt(screenSpacePos);
+
+ private readonly Box box;
+
+ public TestInputConsumer()
+ {
+ Size = new Vector2(30);
+
+ Origin = Anchor.Centre;
+
+ InternalChildren = new Drawable[]
+ {
+ box = new Box
+ {
+ Colour = Color4.Black,
+ RelativeSizeAxes = Axes.Both,
+ },
+ };
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ Position = e.MousePosition;
+ return base.OnMouseMove(e);
+ }
+
+ public bool OnPressed(TestAction action)
+ {
+ box.Colour = Color4.White;
+ return true;
+ }
+
+ public void OnReleased(TestAction action)
+ {
+ box.Colour = Color4.Black;
+ }
+ }
+
+ public class TestRulesetInputManager : RulesetInputManager
+ {
+ public TestRulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ : base(ruleset, variant, unique)
+ {
+ }
+
+ protected override KeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ => new TestKeyBindingContainer();
+
+ internal class TestKeyBindingContainer : KeyBindingContainer
+ {
+ public override IEnumerable DefaultKeyBindings => new[]
+ {
+ new KeyBinding(InputKey.MouseLeft, TestAction.Down),
+ };
+ }
+ }
+
+ public class TestReplayFrame : ReplayFrame
+ {
+ public Vector2 Position;
+
+ public List Actions = new List();
+
+ public TestReplayFrame(double time, Vector2 position, params TestAction[] actions)
+ : base(time)
+ {
+ Position = position;
+ Actions.AddRange(actions);
+ }
+ }
+
+ public enum TestAction
+ {
+ Down,
+ }
+
+ internal class TestReplayRecorder : ReplayRecorder
+ {
+ public TestReplayRecorder(Replay target)
+ : base(target)
+ {
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame)
+ => new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
+ }
+ }
+}
diff --git a/osu.Game.Tests/Gameplay/TestSceneReplayRecording.cs b/osu.Game.Tests/Gameplay/TestSceneReplayRecording.cs
new file mode 100644
index 0000000000..057d026132
--- /dev/null
+++ b/osu.Game.Tests/Gameplay/TestSceneReplayRecording.cs
@@ -0,0 +1,221 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Bindings;
+using osu.Framework.Input.Events;
+using osu.Framework.Input.StateChanges;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Replays;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Replays;
+using osu.Game.Rulesets.UI;
+using osu.Game.Tests.Visual;
+using osu.Game.Tests.Visual.UserInterface;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Tests.Gameplay
+{
+ public class TestSceneReplayRecording : OsuTestScene
+ {
+ private readonly TestRulesetInputManager playbackManager;
+
+ private readonly TestRulesetInputManager recordingManager;
+
+ public TestSceneReplayRecording()
+ {
+ Replay replay = new Replay();
+
+ Add(new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
+ {
+ Recorder = new TestReplayRecorder(replay)
+ {
+ ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos)
+ },
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = Color4.Brown,
+ RelativeSizeAxes = Axes.Both,
+ },
+ new OsuSpriteText
+ {
+ Text = "Recording",
+ Scale = new Vector2(3),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new TestConsumer()
+ }
+ },
+ }
+ },
+ new Drawable[]
+ {
+ playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
+ {
+ ReplayInputHandler = new TestFramedReplayInputHandler(replay)
+ {
+ GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
+ },
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Colour = Color4.DarkBlue,
+ RelativeSizeAxes = Axes.Both,
+ },
+ new OsuSpriteText
+ {
+ Text = "Playback",
+ Scale = new Vector2(3),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new TestConsumer()
+ }
+ },
+ }
+ }
+ }
+ });
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ playbackManager.ReplayInputHandler.SetFrameFromTime(Time.Current - 500);
+ }
+ }
+
+ public class TestFramedReplayInputHandler : FramedReplayInputHandler
+ {
+ public TestFramedReplayInputHandler(Replay replay)
+ : base(replay)
+ {
+ }
+
+ public override List GetPendingInputs()
+ {
+ return new List
+ {
+ new MousePositionAbsoluteInput
+ {
+ Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero)
+ },
+ new ReplayState
+ {
+ PressedActions = CurrentFrame?.Actions ?? new List()
+ }
+ };
+ }
+ }
+
+ public class TestConsumer : CompositeDrawable, IKeyBindingHandler
+ {
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent.ReceivePositionalInputAt(screenSpacePos);
+
+ private readonly Box box;
+
+ public TestConsumer()
+ {
+ Size = new Vector2(30);
+
+ Origin = Anchor.Centre;
+
+ InternalChildren = new Drawable[]
+ {
+ box = new Box
+ {
+ Colour = Color4.Black,
+ RelativeSizeAxes = Axes.Both,
+ },
+ };
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ Position = e.MousePosition;
+ return base.OnMouseMove(e);
+ }
+
+ public bool OnPressed(TestAction action)
+ {
+ box.Colour = Color4.White;
+ return true;
+ }
+
+ public void OnReleased(TestAction action)
+ {
+ box.Colour = Color4.Black;
+ }
+ }
+
+ public class TestRulesetInputManager : RulesetInputManager
+ {
+ public TestRulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ : base(ruleset, variant, unique)
+ {
+ }
+
+ protected override KeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ => new TestKeyBindingContainer();
+
+ internal class TestKeyBindingContainer : KeyBindingContainer
+ {
+ public override IEnumerable DefaultKeyBindings => new[]
+ {
+ new KeyBinding(InputKey.MouseLeft, TestAction.Down),
+ };
+ }
+ }
+
+ public class TestReplayFrame : ReplayFrame
+ {
+ public Vector2 Position;
+
+ public List Actions = new List();
+
+ public TestReplayFrame(double time, Vector2 position, params TestAction[] actions)
+ : base(time)
+ {
+ Position = position;
+ Actions.AddRange(actions);
+ }
+ }
+
+ public enum TestAction
+ {
+ Down,
+ }
+
+ internal class TestReplayRecorder : ReplayRecorder
+ {
+ public TestReplayRecorder(Replay target)
+ : base(target)
+ {
+ }
+
+ protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame) =>
+ new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
+ }
+}
diff --git a/osu.Game.Tests/Resources/storyboard_no_video.osu b/osu.Game.Tests/Resources/storyboard_no_video.osu
new file mode 100644
index 0000000000..25f1ff6361
--- /dev/null
+++ b/osu.Game.Tests/Resources/storyboard_no_video.osu
@@ -0,0 +1,31 @@
+osu file format v14
+
+[Events]
+//Background and Video events
+0,0,"BG.jpg",0,0
+Video,0,"video.avi"
+//Break Periods
+//Storyboard Layer 0 (Background)
+//Storyboard Layer 1 (Fail)
+//Storyboard Layer 2 (Pass)
+//Storyboard Layer 3 (Foreground)
+//Storyboard Layer 4 (Overlay)
+//Storyboard Sound Samples
+
+[TimingPoints]
+1674,333.333333333333,4,2,1,70,1,0
+1674,-100,4,2,1,70,0,0
+3340,-100,4,2,1,70,0,0
+3507,-100,4,2,1,70,0,0
+3673,-100,4,2,1,70,0,0
+
+[Colours]
+Combo1 : 240,80,80
+Combo2 : 171,252,203
+Combo3 : 128,128,255
+Combo4 : 249,254,186
+
+[HitObjects]
+148,303,1674,5,6,3:2:0:0:
+378,252,1840,1,0,0:0:0:0:
+389,270,2340,5,2,0:1:0:0:
diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
index a139c3a8c2..90bf419644 100644
--- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
+++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
@@ -236,8 +236,6 @@ namespace osu.Game.Tests.Scores.IO
}
public override IEnumerable Filenames => new[] { "test_file.osr" };
-
- public override Stream GetUnderlyingStream() => new MemoryStream();
}
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
index afeda5fb7c..5ee17aeea2 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs
@@ -3,8 +3,10 @@
using System.ComponentModel;
using System.Linq;
+using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Screens.Play;
+using osu.Game.Screens.Play.Break;
namespace osu.Game.Tests.Visual.Gameplay
{
@@ -23,10 +25,11 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
- AddStep("seek to break time", () => Player.GameplayClockContainer.Seek(Player.BreakOverlay.Breaks.First().StartTime));
+ AddStep("seek to break time", () => Player.GameplayClockContainer.Seek(Player.ChildrenOfType().First().Breaks.First().StartTime));
AddUntilStep("wait for seek to complete", () =>
Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= Player.BreakOverlay.Breaks.First().StartTime);
- AddAssert("test keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
+ AddAssert("keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
+ AddAssert("overlay displays 100% accuracy", () => Player.BreakOverlay.ChildrenOfType().Single().AccuracyDisplay.Current.Value == 1);
AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs
similarity index 80%
rename from osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs
rename to osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs
index 19dce303ea..ff25e609c1 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakTracker.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
+using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Beatmaps.Timing;
using osu.Game.Screens.Play;
@@ -12,14 +13,16 @@ using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay
{
[TestFixture]
- public class TestSceneBreakOverlay : OsuTestScene
+ public class TestSceneBreakTracker : OsuTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
typeof(BreakOverlay),
};
- private readonly TestBreakOverlay breakOverlay;
+ private readonly BreakOverlay breakOverlay;
+
+ private readonly TestBreakTracker breakTracker;
private readonly IReadOnlyList testBreaks = new List
{
@@ -35,9 +38,23 @@ namespace osu.Game.Tests.Visual.Gameplay
},
};
- public TestSceneBreakOverlay()
+ public TestSceneBreakTracker()
{
- Add(breakOverlay = new TestBreakOverlay(true));
+ AddRange(new Drawable[]
+ {
+ breakTracker = new TestBreakTracker(),
+ breakOverlay = new BreakOverlay(true, null)
+ {
+ ProcessCustomClock = false,
+ }
+ });
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ breakOverlay.Clock = breakTracker.Clock;
}
[Test]
@@ -88,7 +105,7 @@ namespace osu.Game.Tests.Visual.Gameplay
loadBreaksStep("multiple breaks", testBreaks);
seekAndAssertBreak("seek to break start", testBreaks[1].StartTime, true);
- AddAssert("is skipped to break #2", () => breakOverlay.CurrentBreakIndex == 1);
+ AddAssert("is skipped to break #2", () => breakTracker.CurrentBreakIndex == 1);
seekAndAssertBreak("seek to break middle", testBreaks[1].StartTime + testBreaks[1].Duration / 2, true);
seekAndAssertBreak("seek to break end", testBreaks[1].EndTime, false);
@@ -110,7 +127,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private void addShowBreakStep(double seconds)
{
- AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = new List
+ AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = breakTracker.Breaks = new List
{
new BreakPeriod
{
@@ -122,12 +139,12 @@ namespace osu.Game.Tests.Visual.Gameplay
private void setClock(bool useManual)
{
- AddStep($"set {(useManual ? "manual" : "realtime")} clock", () => breakOverlay.SwitchClock(useManual));
+ AddStep($"set {(useManual ? "manual" : "realtime")} clock", () => breakTracker.SwitchClock(useManual));
}
private void loadBreaksStep(string breakDescription, IReadOnlyList breaks)
{
- AddStep($"load {breakDescription}", () => breakOverlay.Breaks = breaks);
+ AddStep($"load {breakDescription}", () => breakOverlay.Breaks = breakTracker.Breaks = breaks);
seekAndAssertBreak("seek back to 0", 0, false);
}
@@ -151,17 +168,18 @@ namespace osu.Game.Tests.Visual.Gameplay
private void seekAndAssertBreak(string seekStepDescription, double time, bool shouldBeBreak)
{
- AddStep(seekStepDescription, () => breakOverlay.ManualClockTime = time);
+ AddStep(seekStepDescription, () => breakTracker.ManualClockTime = time);
AddAssert($"is{(!shouldBeBreak ? " not" : string.Empty)} break time", () =>
{
- breakOverlay.ProgressTime();
- return breakOverlay.IsBreakTime.Value == shouldBeBreak;
+ breakTracker.ProgressTime();
+ return breakTracker.IsBreakTime.Value == shouldBeBreak;
});
}
- private class TestBreakOverlay : BreakOverlay
+ private class TestBreakTracker : BreakTracker
{
- private readonly FramedClock framedManualClock;
+ public readonly FramedClock FramedManualClock;
+
private readonly ManualClock manualClock;
private IFrameBasedClock originalClock;
@@ -173,20 +191,19 @@ namespace osu.Game.Tests.Visual.Gameplay
set => manualClock.CurrentTime = value;
}
- public TestBreakOverlay(bool letterboxing)
- : base(letterboxing)
+ public TestBreakTracker()
{
- framedManualClock = new FramedClock(manualClock = new ManualClock());
+ FramedManualClock = new FramedClock(manualClock = new ManualClock());
ProcessCustomClock = false;
}
public void ProgressTime()
{
- framedManualClock.ProcessFrame();
+ FramedManualClock.ProcessFrame();
Update();
}
- public void SwitchClock(bool setManual) => Clock = setManual ? framedManualClock : originalClock;
+ public void SwitchClock(bool setManual) => Clock = setManual ? FramedManualClock : originalClock;
protected override void LoadComplete()
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
index ff8437311e..9f1492a25f 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs
@@ -9,8 +9,12 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Formats;
+using osu.Game.IO;
using osu.Game.Overlays;
+using osu.Game.Storyboards;
using osu.Game.Storyboards.Drawables;
+using osu.Game.Tests.Resources;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Gameplay
@@ -54,7 +58,11 @@ namespace osu.Game.Tests.Visual.Gameplay
State = { Value = Visibility.Visible },
}
});
+ }
+ [Test]
+ public void TestStoryboard()
+ {
AddStep("Restart", restart);
AddToggleStep("Passing", passing =>
{
@@ -62,6 +70,12 @@ namespace osu.Game.Tests.Visual.Gameplay
});
}
+ [Test]
+ public void TestStoryboardMissingVideo()
+ {
+ AddStep("Load storyboard with missing video", loadStoryboardNoVideo);
+ }
+
[BackgroundDependencyLoader]
private void load()
{
@@ -94,5 +108,28 @@ namespace osu.Game.Tests.Visual.Gameplay
storyboardContainer.Add(storyboard);
decoupledClock.ChangeSource(working.Track);
}
+
+ private void loadStoryboardNoVideo()
+ {
+ if (storyboard != null)
+ storyboardContainer.Remove(storyboard);
+
+ var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
+ storyboardContainer.Clock = decoupledClock;
+
+ Storyboard sb;
+
+ using (var str = TestResources.OpenResource("storyboard_no_video.osu"))
+ using (var bfr = new LineBufferedReader(str))
+ {
+ var decoder = new LegacyStoryboardDecoder();
+ sb = decoder.Decode(bfr);
+ }
+
+ storyboard = sb.CreateDrawable(Beatmap.Value);
+
+ storyboardContainer.Add(storyboard);
+ decoupledClock.ChangeSource(Beatmap.Value.Track);
+ }
}
}
diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
index 5870ef9813..1ad4d9dca9 100644
--- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
+++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs
@@ -64,6 +64,8 @@ namespace osu.Game.Tests.Visual.Menus
introStack.Push(CreateScreen());
});
+
+ AddUntilStep("wait for menu", () => introStack.CurrentScreen is MainMenu);
}
protected abstract IScreen CreateScreen();
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoader.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoader.cs
index b3064ba9be..c44363d9ea 100644
--- a/osu.Game.Tests/Visual/Menus/TestSceneLoader.cs
+++ b/osu.Game.Tests/Visual/Menus/TestSceneLoader.cs
@@ -36,8 +36,6 @@ namespace osu.Game.Tests.Visual.Menus
[Test]
public void TestInstantLoad()
{
- // visual only, very impossible to test this using asserts.
-
AddStep("load immediately", () =>
{
loader = new TestLoader();
@@ -46,12 +44,17 @@ namespace osu.Game.Tests.Visual.Menus
LoadScreen(loader);
});
- AddAssert("spinner did not display", () => loader.LoadingSpinner?.Alpha == 0);
+ spinnerNotPresentOrHidden();
AddUntilStep("loaded", () => loader.ScreenLoaded);
AddUntilStep("not current", () => !loader.IsCurrentScreen());
+
+ spinnerNotPresentOrHidden();
}
+ private void spinnerNotPresentOrHidden() =>
+ AddAssert("spinner did not display", () => loader.LoadingSpinner == null || loader.LoadingSpinner.Alpha == 0);
+
[Test]
public void TestDelayedLoad()
{
diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
index ccae778745..a38f045e7f 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Online
private readonly Bindable status = new Bindable();
private UserGridPanel peppy;
- private UserListPanel evast;
+ private TestUserListPanel evast;
[Resolved]
private RulesetStore rulesetStore { get; set; }
@@ -38,6 +38,9 @@ namespace osu.Game.Tests.Visual.Online
{
UserGridPanel flyte;
+ activity.Value = null;
+ status.Value = null;
+
Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
@@ -63,7 +66,7 @@ namespace osu.Game.Tests.Visual.Online
IsSupporter = true,
SupportLevel = 3,
}) { Width = 300 },
- evast = new UserListPanel(new User
+ evast = new TestUserListPanel(new User
{
Username = @"Evast",
Id = 8195163,
@@ -96,7 +99,7 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestUserActivity()
{
- AddStep("set online status", () => peppy.Status.Value = evast.Status.Value = new UserStatusOnline());
+ AddStep("set online status", () => status.Value = new UserStatusOnline());
AddStep("idle", () => activity.Value = null);
AddStep("spectating", () => activity.Value = new UserActivity.Spectating());
@@ -109,6 +112,29 @@ namespace osu.Game.Tests.Visual.Online
AddStep("modding", () => activity.Value = new UserActivity.Modding());
}
+ [Test]
+ public void TestUserActivityChange()
+ {
+ AddAssert("visit message is visible", () => evast.LastVisitMessage.IsPresent);
+ AddStep("set online status", () => status.Value = new UserStatusOnline());
+ AddAssert("visit message is not visible", () => !evast.LastVisitMessage.IsPresent);
+ AddStep("set choosing activity", () => activity.Value = new UserActivity.ChoosingBeatmap());
+ AddStep("set offline status", () => status.Value = new UserStatusOffline());
+ AddAssert("visit message is visible", () => evast.LastVisitMessage.IsPresent);
+ AddStep("set online status", () => status.Value = new UserStatusOnline());
+ AddAssert("visit message is not visible", () => !evast.LastVisitMessage.IsPresent);
+ }
+
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.SoloGame(null, rulesetStore.GetRuleset(rulesetId));
+
+ private class TestUserListPanel : UserListPanel
+ {
+ public TestUserListPanel(User user)
+ : base(user)
+ {
+ }
+
+ public new TextFlowContainer LastVisitMessage => base.LastVisitMessage;
+ }
}
}
diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs
index 53ce5def32..90c91eb007 100644
--- a/osu.Game.Tests/WaveformTestBeatmap.cs
+++ b/osu.Game.Tests/WaveformTestBeatmap.cs
@@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
@@ -51,8 +50,6 @@ namespace osu.Game.Tests
protected override Texture GetBackground() => null;
- protected override VideoSprite GetVideo() => null;
-
protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile));
protected override Track GetTrack() => trackStore.Get(firstAudioFile);
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 31869f9310..abb3f8ac42 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -14,7 +14,6 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Framework.Lists;
using osu.Framework.Logging;
using osu.Framework.Platform;
@@ -403,7 +402,6 @@ namespace osu.Game.Beatmaps
protected override IBeatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null;
- protected override VideoSprite GetVideo() => null;
protected override Track GetTrack() => null;
}
diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
index 1991770518..e62a9bb39d 100644
--- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
@@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Game.Beatmaps.Formats;
@@ -67,24 +66,6 @@ namespace osu.Game.Beatmaps
}
}
- protected override VideoSprite GetVideo()
- {
- if (Metadata?.VideoFile == null)
- return null;
-
- try
- {
- var stream = textureStore.GetStream(getPathForFile(Metadata.VideoFile));
-
- return stream == null ? null : new VideoSprite(stream);
- }
- catch (Exception e)
- {
- Logger.Error(e, "Video failed to load");
- return null;
- }
- }
-
protected override Track GetTrack()
{
try
diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs
index 9267527d79..001f319307 100644
--- a/osu.Game/Beatmaps/BeatmapMetadata.cs
+++ b/osu.Game/Beatmaps/BeatmapMetadata.cs
@@ -52,7 +52,6 @@ namespace osu.Game.Beatmaps
public int PreviewTime { get; set; }
public string AudioFile { get; set; }
public string BackgroundFile { get; set; }
- public string VideoFile { get; set; }
public override string ToString() => $"{Artist} - {Title} ({Author})";
@@ -82,8 +81,7 @@ namespace osu.Game.Beatmaps
&& Tags == other.Tags
&& PreviewTime == other.PreviewTime
&& AudioFile == other.AudioFile
- && BackgroundFile == other.BackgroundFile
- && VideoFile == other.VideoFile;
+ && BackgroundFile == other.BackgroundFile;
}
}
}
diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
index bfcc38e4a9..8080e94075 100644
--- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
@@ -7,7 +7,6 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
@@ -45,8 +44,6 @@ namespace osu.Game.Beatmaps
protected override Texture GetBackground() => textures?.Get(@"Backgrounds/bg4");
- protected override VideoSprite GetVideo() => null;
-
protected override Track GetTrack() => GetVirtualTrack();
private class DummyRulesetInfo : RulesetInfo
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 4b01b2490e..f5b27eddd2 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -6,11 +6,11 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using osu.Framework.Extensions;
-using osu.Game.Beatmaps.Timing;
-using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.IO;
using osu.Game.Beatmaps.Legacy;
+using osu.Game.Beatmaps.Timing;
+using osu.Game.IO;
+using osu.Game.Rulesets.Objects.Legacy;
namespace osu.Game.Beatmaps.Formats
{
@@ -303,10 +303,6 @@ namespace osu.Game.Beatmaps.Formats
beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]);
break;
- case LegacyEventType.Video:
- beatmap.BeatmapInfo.Metadata.VideoFile = CleanFilename(split[2]);
- break;
-
case LegacyEventType.Break:
double start = getOffsetTime(Parsing.ParseDouble(split[1]));
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 09f40ce7b6..ec2ca30535 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -133,9 +133,6 @@ namespace osu.Game.Beatmaps.Formats
if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.BackgroundFile))
writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Background},0,\"{beatmap.BeatmapInfo.Metadata.BackgroundFile}\",0,0"));
- if (!string.IsNullOrEmpty(beatmap.BeatmapInfo.Metadata.VideoFile))
- writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Video},0,\"{beatmap.BeatmapInfo.Metadata.VideoFile}\",0,0"));
-
foreach (var b in beatmap.Breaks)
writer.WriteLine(FormattableString.Invariant($"{(int)LegacyEventType.Break},{b.StartTime},{b.EndTime}"));
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index c81f933bca..269449ef80 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -4,13 +4,13 @@
using System;
using System.Collections.Generic;
using System.IO;
-using osuTK;
-using osuTK.Graphics;
using osu.Framework.Graphics;
+using osu.Framework.Utils;
+using osu.Game.Beatmaps.Legacy;
using osu.Game.IO;
using osu.Game.Storyboards;
-using osu.Game.Beatmaps.Legacy;
-using osu.Framework.Utils;
+using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Beatmaps.Formats
{
@@ -87,6 +87,15 @@ namespace osu.Game.Beatmaps.Formats
switch (type)
{
+ case LegacyEventType.Video:
+ {
+ var offset = Parsing.ParseInt(split[1]);
+ var path = CleanFilename(split[2]);
+
+ storyboard.GetLayer("Video").Add(new StoryboardVideo(path, offset));
+ break;
+ }
+
case LegacyEventType.Sprite:
{
var layer = parseLayer(split[1]);
diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs
index 526bc668af..31975157a0 100644
--- a/osu.Game/Beatmaps/IWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
@@ -27,11 +26,6 @@ namespace osu.Game.Beatmaps
///
Texture Background { get; }
- ///
- /// Retrieves the video background file for this .
- ///
- VideoSprite Video { get; }
-
///
/// Retrieves the audio track for this .
///
diff --git a/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs b/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs
index 5237445640..48e8bdbb76 100644
--- a/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs
+++ b/osu.Game/Beatmaps/Legacy/LegacyStoryLayer.cs
@@ -8,6 +8,7 @@ namespace osu.Game.Beatmaps.Legacy
Background = 0,
Fail = 1,
Pass = 2,
- Foreground = 3
+ Foreground = 3,
+ Video = 4
}
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index dd4f893ac2..f30340956a 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -1,23 +1,22 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Audio.Track;
-using osu.Framework.Graphics.Textures;
-using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
-using osu.Game.Storyboards;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Audio;
+using osu.Framework.Audio.Track;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Logging;
using osu.Framework.Statistics;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
-using osu.Framework.Graphics.Video;
-using osu.Framework.Logging;
+using osu.Game.Storyboards;
namespace osu.Game.Beatmaps
{
@@ -234,10 +233,6 @@ namespace osu.Game.Beatmaps
protected abstract Texture GetBackground();
private readonly RecyclableLazy background;
- public VideoSprite Video => GetVideo();
-
- protected abstract VideoSprite GetVideo();
-
public bool TrackLoaded => track.IsResultAvailable;
public Track Track => track.Value;
protected abstract Track GetTrack();
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 21de654670..41f6747b74 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -70,7 +70,6 @@ namespace osu.Game.Configuration
Set(OsuSetting.ShowFpsDisplay, false);
Set(OsuSetting.ShowStoryboard, true);
- Set(OsuSetting.ShowVideoBackground, true);
Set(OsuSetting.BeatmapSkins, true);
Set(OsuSetting.BeatmapHitsounds, true);
@@ -176,7 +175,6 @@ namespace osu.Game.Configuration
BlurLevel,
LightenDuringBreaks,
ShowStoryboard,
- ShowVideoBackground,
KeyOverlay,
ScoreMeter,
FloatingComments,
diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs
index 65c104b92f..39c1fdad52 100644
--- a/osu.Game/Graphics/Containers/UserDimContainer.cs
+++ b/osu.Game/Graphics/Containers/UserDimContainer.cs
@@ -40,7 +40,7 @@ namespace osu.Game.Graphics.Containers
///
/// Whether player is in break time.
- /// Must be bound to to allow for dim adjustments in gameplay.
+ /// Must be bound to to allow for dim adjustments in gameplay.
///
public readonly IBindable IsBreakTime = new Bindable();
@@ -55,8 +55,6 @@ namespace osu.Game.Graphics.Containers
protected Bindable ShowStoryboard { get; private set; }
- protected Bindable ShowVideo { get; private set; }
-
private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0;
protected float DimLevel => Math.Max(EnableUserDim.Value && !IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0);
@@ -79,14 +77,12 @@ namespace osu.Game.Graphics.Containers
UserDimLevel = config.GetBindable(OsuSetting.DimLevel);
LightenDuringBreaks = config.GetBindable(OsuSetting.LightenDuringBreaks);
ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard);
- ShowVideo = config.GetBindable(OsuSetting.ShowVideoBackground);
EnableUserDim.ValueChanged += _ => UpdateVisuals();
UserDimLevel.ValueChanged += _ => UpdateVisuals();
LightenDuringBreaks.ValueChanged += _ => UpdateVisuals();
IsBreakTime.ValueChanged += _ => UpdateVisuals();
ShowStoryboard.ValueChanged += _ => UpdateVisuals();
- ShowVideo.ValueChanged += _ => UpdateVisuals();
StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals();
IgnoreUserSettings.ValueChanged += _ => UpdateVisuals();
}
diff --git a/osu.Game/Graphics/Sprites/OsuSpriteText.cs b/osu.Game/Graphics/Sprites/OsuSpriteText.cs
index cd988c347b..76e46513ba 100644
--- a/osu.Game/Graphics/Sprites/OsuSpriteText.cs
+++ b/osu.Game/Graphics/Sprites/OsuSpriteText.cs
@@ -1,9 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Transforms;
namespace osu.Game.Graphics.Sprites
{
@@ -15,23 +13,4 @@ namespace osu.Game.Graphics.Sprites
Font = OsuFont.Default;
}
}
-
- public static class OsuSpriteTextTransformExtensions
- {
- ///
- /// Sets Text to a new value after a duration.
- ///
- /// A to which further transforms can be added.
- public static TransformSequence TransformTextTo(this T spriteText, string newText, double duration = 0, Easing easing = Easing.None)
- where T : OsuSpriteText
- => spriteText.TransformTo(nameof(OsuSpriteText.Text), newText, duration, easing);
-
- ///
- /// Sets Text to a new value after a duration.
- ///
- /// A to which further transforms can be added.
- public static TransformSequence TransformTextTo(this TransformSequence t, string newText, double duration = 0, Easing easing = Easing.None)
- where T : OsuSpriteText
- => t.Append(o => o.TransformTextTo(newText, duration, easing));
- }
}
diff --git a/osu.Game/IO/Archives/ArchiveReader.cs b/osu.Game/IO/Archives/ArchiveReader.cs
index 4ee7a19ebc..a30f961daf 100644
--- a/osu.Game/IO/Archives/ArchiveReader.cs
+++ b/osu.Game/IO/Archives/ArchiveReader.cs
@@ -45,7 +45,5 @@ namespace osu.Game.IO.Archives
return buffer;
}
}
-
- public abstract Stream GetUnderlyingStream();
}
}
diff --git a/osu.Game/IO/Archives/LegacyByteArrayReader.cs b/osu.Game/IO/Archives/LegacyByteArrayReader.cs
new file mode 100644
index 0000000000..0c3620403f
--- /dev/null
+++ b/osu.Game/IO/Archives/LegacyByteArrayReader.cs
@@ -0,0 +1,30 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using System.IO;
+
+namespace osu.Game.IO.Archives
+{
+ ///
+ /// Allows reading a single file from the provided stream.
+ ///
+ public class LegacyByteArrayReader : ArchiveReader
+ {
+ private readonly byte[] content;
+
+ public LegacyByteArrayReader(byte[] content, string filename)
+ : base(filename)
+ {
+ this.content = content;
+ }
+
+ public override Stream GetStream(string name) => new MemoryStream(content);
+
+ public override void Dispose()
+ {
+ }
+
+ public override IEnumerable Filenames => new[] { Name };
+ }
+}
diff --git a/osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs b/osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs
index eff02ae7a5..dfae58aed7 100644
--- a/osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs
+++ b/osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs
@@ -28,7 +28,5 @@ namespace osu.Game.IO.Archives
}
public override IEnumerable Filenames => Directory.GetFiles(path, "*", SearchOption.AllDirectories).Select(f => f.Replace(path, string.Empty).Trim(Path.DirectorySeparatorChar)).ToArray();
-
- public override Stream GetUnderlyingStream() => null;
}
}
diff --git a/osu.Game/IO/Archives/LegacyFileArchiveReader.cs b/osu.Game/IO/Archives/LegacyFileArchiveReader.cs
index bd5f9cbd07..72e5a21079 100644
--- a/osu.Game/IO/Archives/LegacyFileArchiveReader.cs
+++ b/osu.Game/IO/Archives/LegacyFileArchiveReader.cs
@@ -28,7 +28,5 @@ namespace osu.Game.IO.Archives
}
public override IEnumerable Filenames => new[] { Name };
-
- public override Stream GetUnderlyingStream() => null;
}
}
diff --git a/osu.Game/IO/Archives/ZipArchiveReader.cs b/osu.Game/IO/Archives/ZipArchiveReader.cs
index 35f38ea7e8..80dfa104f3 100644
--- a/osu.Game/IO/Archives/ZipArchiveReader.cs
+++ b/osu.Game/IO/Archives/ZipArchiveReader.cs
@@ -45,7 +45,5 @@ namespace osu.Game.IO.Archives
}
public override IEnumerable Filenames => archive.Entries.Select(e => e.Key).ExcludeSystemFileNames();
-
- public override Stream GetUnderlyingStream() => archiveStream;
}
}
diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
index ea2811e5cd..3089040f96 100644
--- a/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Graphics/DetailSettings.cs
@@ -18,15 +18,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{
new SettingsCheckbox
{
- LabelText = "Storyboards",
+ LabelText = "Storyboard / Video",
Bindable = config.GetBindable(OsuSetting.ShowStoryboard)
},
new SettingsCheckbox
- {
- LabelText = "Video",
- Bindable = config.GetBindable(OsuSetting.ShowVideoBackground)
- },
- new SettingsCheckbox
{
LabelText = "Hit Lighting",
Bindable = config.GetBindable(OsuSetting.HitLighting)
diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
index 59d39a1c3c..e7f2f21465 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Configuration;
@@ -56,24 +57,32 @@ namespace osu.Game.Overlays.Settings.Sections.Input
},
};
- rawInputToggle.ValueChanged += enabled =>
+ if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows)
{
- // this is temporary until we support per-handler settings.
- const string raw_mouse_handler = @"OsuTKRawMouseHandler";
- const string standard_mouse_handler = @"OsuTKMouseHandler";
-
- ignoredInputHandler.Value = enabled.NewValue ? standard_mouse_handler : raw_mouse_handler;
- };
-
- ignoredInputHandler = config.GetBindable(FrameworkSetting.IgnoredInputHandlers);
- ignoredInputHandler.ValueChanged += handler =>
+ rawInputToggle.Disabled = true;
+ sensitivity.Bindable.Disabled = true;
+ }
+ else
{
- bool raw = !handler.NewValue.Contains("Raw");
- rawInputToggle.Value = raw;
- sensitivity.Bindable.Disabled = !raw;
- };
+ rawInputToggle.ValueChanged += enabled =>
+ {
+ // this is temporary until we support per-handler settings.
+ const string raw_mouse_handler = @"OsuTKRawMouseHandler";
+ const string standard_mouse_handler = @"OsuTKMouseHandler";
- ignoredInputHandler.TriggerChange();
+ ignoredInputHandler.Value = enabled.NewValue ? standard_mouse_handler : raw_mouse_handler;
+ };
+
+ ignoredInputHandler = config.GetBindable(FrameworkSetting.IgnoredInputHandlers);
+ ignoredInputHandler.ValueChanged += handler =>
+ {
+ bool raw = !handler.NewValue.Contains("Raw");
+ rawInputToggle.Value = raw;
+ sensitivity.Bindable.Disabled = !raw;
+ };
+
+ ignoredInputHandler.TriggerChange();
+ }
}
private class SensitivitySetting : SettingsSlider
diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs
index cd08aee453..cf8128301c 100644
--- a/osu.Game/Rulesets/Mods/ModCinema.cs
+++ b/osu.Game/Rulesets/Mods/ModCinema.cs
@@ -39,7 +39,6 @@ namespace osu.Game.Rulesets.Mods
{
player.Background.EnableUserDim.Value = false;
- player.DimmableVideo.IgnoreUserSettings.Value = true;
player.DimmableStoryboard.IgnoreUserSettings.Value = true;
player.BreakOverlay.Hide();
diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs
new file mode 100644
index 0000000000..da55ab3fbf
--- /dev/null
+++ b/osu.Game/Rulesets/Mods/ModRandom.cs
@@ -0,0 +1,17 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
+
+namespace osu.Game.Rulesets.Mods
+{
+ public abstract class ModRandom : Mod
+ {
+ public override string Name => "Random";
+ public override string Acronym => "RD";
+ public override ModType Type => ModType.Conversion;
+ public override IconUsage? Icon => OsuIcon.Dice;
+ public override double ScoreMultiplier => 1;
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index aa29e42fac..5b5802fa9d 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -344,7 +344,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// Plays all the hit sounds for this .
/// This is invoked automatically when this is hit.
///
- public void PlaySamples() => Samples?.Play();
+ public virtual void PlaySamples() => Samples?.Play();
protected override void Update()
{
diff --git a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs
index c2947c0aca..d9aa615c6e 100644
--- a/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs
+++ b/osu.Game/Rulesets/Replays/Types/IConvertibleReplayFrame.cs
@@ -17,6 +17,12 @@ namespace osu.Game.Rulesets.Replays.Types
/// The to extract values from.
/// The beatmap.
/// The last post-conversion , used to fill in missing delta information. May be null.
- void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null);
+ void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null);
+
+ ///
+ /// Populates this using values from a .
+ ///
+ /// The beatmap.
+ LegacyReplayFrame ToLegacy(IBeatmap beatmap);
}
}
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index c38a5c6af7..58f598a203 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -42,9 +42,63 @@ namespace osu.Game.Rulesets
///
/// Converts mods from legacy enum values. Do not override if you're not a legacy ruleset.
///
- /// The legacy enum which will be converted
- /// An enumerable of constructed s
- public virtual IEnumerable ConvertLegacyMods(LegacyMods mods) => Array.Empty();
+ /// The legacy enum which will be converted.
+ /// An enumerable of constructed s.
+ public virtual IEnumerable ConvertFromLegacyMods(LegacyMods mods) => Array.Empty();
+
+ ///
+ /// Converts mods to legacy enum values. Do not override if you're not a legacy ruleset.
+ ///
+ /// The mods which will be converted.
+ /// A single bitwise enumerable value representing (to the best of our ability) the mods.
+ public virtual LegacyMods ConvertToLegacyMods(Mod[] mods)
+ {
+ var value = LegacyMods.None;
+
+ foreach (var mod in mods)
+ {
+ switch (mod)
+ {
+ case ModNoFail _:
+ value |= LegacyMods.NoFail;
+ break;
+
+ case ModEasy _:
+ value |= LegacyMods.Easy;
+ break;
+
+ case ModHidden _:
+ value |= LegacyMods.Hidden;
+ break;
+
+ case ModHardRock _:
+ value |= LegacyMods.HardRock;
+ break;
+
+ case ModSuddenDeath _:
+ value |= LegacyMods.SuddenDeath;
+ break;
+
+ case ModDoubleTime _:
+ value |= LegacyMods.DoubleTime;
+ break;
+
+ case ModRelax _:
+ value |= LegacyMods.Relax;
+ break;
+
+ case ModHalfTime _:
+ value |= LegacyMods.HalfTime;
+ break;
+
+ case ModFlashlight _:
+ value |= LegacyMods.Flashlight;
+ break;
+ }
+ }
+
+ return value;
+ }
public ModAutoplay GetAutoplayMod() => GetAllMods().OfType().First();
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index d0a2722f58..5062c92afe 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -72,9 +72,9 @@ namespace osu.Game.Rulesets.UI
///
public override Playfield Playfield => playfield.Value;
- private Container overlays;
+ public override Container Overlays { get; } = new Container { RelativeSizeAxes = Axes.Both };
- public override Container Overlays => overlays;
+ public override Container FrameStableComponents { get; } = new Container { RelativeSizeAxes = Axes.Both };
public override GameplayClock FrameStableClock => frameStabilityContainer.GameplayClock;
@@ -187,11 +187,12 @@ namespace osu.Game.Rulesets.UI
FrameStablePlayback = FrameStablePlayback,
Children = new Drawable[]
{
+ FrameStableComponents,
KeyBindingInputManager
.WithChild(CreatePlayfieldAdjustmentContainer()
.WithChild(Playfield)
),
- overlays = new Container { RelativeSizeAxes = Axes.Both }
+ Overlays,
}
},
};
@@ -262,6 +263,21 @@ namespace osu.Game.Rulesets.UI
Playfield.Add(drawableObject);
}
+ public override void SetRecordTarget(Replay recordingReplay)
+ {
+ if (!(KeyBindingInputManager is IHasRecordingHandler recordingInputManager))
+ throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports recording is not available");
+
+ var recorder = CreateReplayRecorder(recordingReplay);
+
+ if (recorder == null)
+ return;
+
+ recorder.ScreenSpaceToGamefield = Playfield.ScreenSpaceToGamefield;
+
+ recordingInputManager.Recorder = recorder;
+ }
+
public override void SetReplayScore(Score replayScore)
{
if (!(KeyBindingInputManager is IHasReplayHandler replayInputManager))
@@ -302,6 +318,8 @@ namespace osu.Game.Rulesets.UI
protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null;
+ protected virtual ReplayRecorder CreateReplayRecorder(Replay replay) => null;
+
///
/// Creates a Playfield.
///
@@ -389,10 +407,15 @@ namespace osu.Game.Rulesets.UI
public abstract Playfield Playfield { get; }
///
- /// Place to put drawables above hit objects but below UI.
+ /// Content to be placed above hitobjects. Will be affected by frame stability.
///
public abstract Container Overlays { get; }
+ ///
+ /// Components to be run potentially multiple times in line with frame-stable gameplay.
+ ///
+ public abstract Container FrameStableComponents { get; }
+
///
/// The frame-stable clock which is being used for playfield display.
///
@@ -470,6 +493,12 @@ namespace osu.Game.Rulesets.UI
/// The replay, null for local input.
public abstract void SetReplayScore(Score replayScore);
+ ///
+ /// Sets a replay to be used to record gameplay.
+ ///
+ /// The target to be recorded to.
+ public abstract void SetRecordTarget(Replay recordingReplay);
+
///
/// Invoked when the interactive user requests resuming from a paused state.
/// Allows potentially delaying the resume process until an interaction is performed.
diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
index e569bb8459..3ba28aad45 100644
--- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
+++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.UI
{
///
/// A container which consumes a parent gameplay clock and standardises frame counts for children.
- /// Will ensure a minimum of 40 frames per clock second is maintained, regardless of any system lag or seeks.
+ /// Will ensure a minimum of 50 frames per clock second is maintained, regardless of any system lag or seeks.
///
public class FrameStabilityContainer : Container, IHasReplayHandler
{
diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs
index 047047ccfd..8141108aef 100644
--- a/osu.Game/Rulesets/UI/Playfield.cs
+++ b/osu.Game/Rulesets/UI/Playfield.cs
@@ -30,6 +30,11 @@ namespace osu.Game.Rulesets.UI
///
public Func GamefieldToScreenSpace => HitObjectContainer.ToScreenSpace;
+ ///
+ /// A function that converts screen space coordinates to gamefield.
+ ///
+ public Func ScreenSpaceToGamefield => HitObjectContainer.ToLocalSpace;
+
///
/// All the s contained in this and all .
///
diff --git a/osu.Game/Rulesets/UI/ReplayRecorder.cs b/osu.Game/Rulesets/UI/ReplayRecorder.cs
new file mode 100644
index 0000000000..c977639584
--- /dev/null
+++ b/osu.Game/Rulesets/UI/ReplayRecorder.cs
@@ -0,0 +1,85 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Graphics;
+using osu.Framework.Input;
+using osu.Framework.Input.Bindings;
+using osu.Framework.Input.Events;
+using osu.Game.Replays;
+using osu.Game.Rulesets.Replays;
+using osuTK;
+
+namespace osu.Game.Rulesets.UI
+{
+ public abstract class ReplayRecorder : ReplayRecorder, IKeyBindingHandler
+ where T : struct
+ {
+ private readonly Replay target;
+
+ private readonly List pressedActions = new List();
+
+ private InputManager inputManager;
+
+ public int RecordFrameRate = 60;
+
+ protected ReplayRecorder(Replay target)
+ {
+ this.target = target;
+
+ RelativeSizeAxes = Axes.Both;
+
+ Depth = float.MinValue;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ inputManager = GetContainingInputManager();
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ recordFrame(false);
+ return base.OnMouseMove(e);
+ }
+
+ public bool OnPressed(T action)
+ {
+ pressedActions.Add(action);
+ recordFrame(true);
+ return false;
+ }
+
+ public void OnReleased(T action)
+ {
+ pressedActions.Remove(action);
+ recordFrame(true);
+ }
+
+ private void recordFrame(bool important)
+ {
+ var last = target.Frames.LastOrDefault();
+
+ if (!important && last != null && Time.Current - last.Time < (1000d / RecordFrameRate))
+ return;
+
+ var position = ScreenSpaceToGamefield?.Invoke(inputManager.CurrentState.Mouse.Position) ?? inputManager.CurrentState.Mouse.Position;
+
+ var frame = HandleFrame(position, pressedActions, last);
+
+ if (frame != null)
+ target.Frames.Add(frame);
+ }
+
+ protected abstract ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame);
+ }
+
+ public abstract class ReplayRecorder : Component
+ {
+ public Func ScreenSpaceToGamefield;
+ }
+}
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index 41b2739fc5..ba30fe28d5 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@@ -23,9 +24,24 @@ using MouseState = osu.Framework.Input.States.MouseState;
namespace osu.Game.Rulesets.UI
{
- public abstract class RulesetInputManager : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler
+ public abstract class RulesetInputManager : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler, IHasRecordingHandler
where T : struct
{
+ private ReplayRecorder recorder;
+
+ public ReplayRecorder Recorder
+ {
+ set
+ {
+ if (recorder != null)
+ throw new InvalidOperationException("Cannot attach more than one recorder");
+
+ recorder = value;
+
+ KeyBindingContainer.Add(recorder);
+ }
+ }
+
protected override InputState CreateInitialState()
{
var state = base.CreateInitialState();
@@ -148,7 +164,7 @@ namespace osu.Game.Rulesets.UI
#endregion
- protected virtual RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ protected virtual KeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new RulesetKeyBindingContainer(ruleset, variant, unique);
public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer
@@ -168,6 +184,11 @@ namespace osu.Game.Rulesets.UI
ReplayInputHandler ReplayInputHandler { get; set; }
}
+ public interface IHasRecordingHandler
+ {
+ public ReplayRecorder Recorder { set; }
+ }
+
///
/// Supports attaching a .
/// Keys will be populated automatically and a receptor will be injected inside.
diff --git a/osu.Game/Scoring/Legacy/DatabasedLegacyScoreParser.cs b/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs
similarity index 74%
rename from osu.Game/Scoring/Legacy/DatabasedLegacyScoreParser.cs
rename to osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs
index 2115d784a0..9b590f56dd 100644
--- a/osu.Game/Scoring/Legacy/DatabasedLegacyScoreParser.cs
+++ b/osu.Game/Scoring/Legacy/DatabasedLegacyScoreDecoder.cs
@@ -7,15 +7,15 @@ using osu.Game.Rulesets;
namespace osu.Game.Scoring.Legacy
{
///
- /// A which retrieves the applicable and
+ /// A which retrieves the applicable and
/// for the score from the database.
///
- public class DatabasedLegacyScoreParser : LegacyScoreParser
+ public class DatabasedLegacyScoreDecoder : LegacyScoreDecoder
{
private readonly RulesetStore rulesets;
private readonly BeatmapManager beatmaps;
- public DatabasedLegacyScoreParser(RulesetStore rulesets, BeatmapManager beatmaps)
+ public DatabasedLegacyScoreDecoder(RulesetStore rulesets, BeatmapManager beatmaps)
{
this.rulesets = rulesets;
this.beatmaps = beatmaps;
diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs
similarity index 97%
rename from osu.Game/Scoring/Legacy/LegacyScoreParser.cs
rename to osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs
index 19d8410cc2..c356dd246d 100644
--- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs
+++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs
@@ -19,7 +19,7 @@ using SharpCompress.Compressors.LZMA;
namespace osu.Game.Scoring.Legacy
{
- public abstract class LegacyScoreParser
+ public abstract class LegacyScoreDecoder
{
private IBeatmap currentBeatmap;
private Ruleset currentRuleset;
@@ -45,9 +45,6 @@ namespace osu.Game.Scoring.Legacy
if (workingBeatmap is DummyWorkingBeatmap)
throw new BeatmapNotFoundException();
- currentBeatmap = workingBeatmap.Beatmap;
- scoreInfo.Beatmap = currentBeatmap.BeatmapInfo;
-
scoreInfo.User = new User { Username = sr.ReadString() };
// MD5Hash
@@ -66,7 +63,10 @@ namespace osu.Game.Scoring.Legacy
/* score.Perfect = */
sr.ReadBoolean();
- scoreInfo.Mods = currentRuleset.ConvertLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
+ scoreInfo.Mods = currentRuleset.ConvertFromLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
+
+ currentBeatmap = workingBeatmap.GetPlayableBeatmap(currentRuleset.RulesetInfo, scoreInfo.Mods);
+ scoreInfo.Beatmap = currentBeatmap.BeatmapInfo;
/* score.HpGraphString = */
sr.ReadString();
@@ -264,7 +264,7 @@ namespace osu.Game.Scoring.Legacy
if (convertible == null)
throw new InvalidOperationException($"Legacy replay cannot be converted for the ruleset: {currentRuleset.Description}");
- convertible.ConvertFrom(currentFrame, currentBeatmap, lastFrame);
+ convertible.FromLegacy(currentFrame, currentBeatmap, lastFrame);
var frame = (ReplayFrame)convertible;
frame.Time = currentFrame.Time;
diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs
new file mode 100644
index 0000000000..db7e51e833
--- /dev/null
+++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs
@@ -0,0 +1,114 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+using osu.Framework.Extensions;
+using osu.Game.Beatmaps;
+using osu.Game.IO.Legacy;
+using osu.Game.Replays.Legacy;
+using osu.Game.Rulesets.Replays.Types;
+using SharpCompress.Compressors.LZMA;
+
+namespace osu.Game.Scoring.Legacy
+{
+ public class LegacyScoreEncoder
+ {
+ public const int LATEST_VERSION = 128;
+
+ private readonly Score score;
+ private readonly IBeatmap beatmap;
+
+ public LegacyScoreEncoder(Score score, IBeatmap beatmap)
+ {
+ this.score = score;
+ this.beatmap = beatmap;
+
+ if (score.ScoreInfo.Beatmap.RulesetID < 0 || score.ScoreInfo.Beatmap.RulesetID > 3)
+ throw new ArgumentException("Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score));
+ }
+
+ public void Encode(Stream stream)
+ {
+ using (SerializationWriter sw = new SerializationWriter(stream))
+ {
+ sw.Write((byte)(score.ScoreInfo.Ruleset.ID ?? 0));
+ sw.Write(LATEST_VERSION);
+ sw.Write(score.ScoreInfo.Beatmap.MD5Hash);
+ sw.Write(score.ScoreInfo.UserString);
+ sw.Write($"lazer-{score.ScoreInfo.UserString}-{score.ScoreInfo.Date}".ComputeMD5Hash());
+ sw.Write((ushort)(score.ScoreInfo.GetCount300() ?? 0));
+ sw.Write((ushort)(score.ScoreInfo.GetCount100() ?? 0));
+ sw.Write((ushort)(score.ScoreInfo.GetCount50() ?? 0));
+ sw.Write((ushort)(score.ScoreInfo.GetCountGeki() ?? 0));
+ sw.Write((ushort)(score.ScoreInfo.GetCountKatu() ?? 0));
+ sw.Write((ushort)(score.ScoreInfo.GetCountMiss() ?? 0));
+ sw.Write((int)(score.ScoreInfo.TotalScore));
+ sw.Write((ushort)score.ScoreInfo.MaxCombo);
+ sw.Write(score.ScoreInfo.Combo == score.ScoreInfo.MaxCombo);
+ sw.Write((int)score.ScoreInfo.Ruleset.CreateInstance().ConvertToLegacyMods(score.ScoreInfo.Mods));
+
+ sw.Write(getHpGraphFormatted());
+ sw.Write(score.ScoreInfo.Date.DateTime);
+ sw.WriteByteArray(createReplayData());
+ sw.Write((long)0);
+ writeModSpecificData(score.ScoreInfo, sw);
+ }
+ }
+
+ private void writeModSpecificData(ScoreInfo score, SerializationWriter sw)
+ {
+ }
+
+ private byte[] createReplayData()
+ {
+ var content = new ASCIIEncoding().GetBytes(replayStringContent);
+
+ using (var outStream = new MemoryStream())
+ {
+ using (var lzma = new LzmaStream(new LzmaEncoderProperties(false, 1 << 21, 255), false, outStream))
+ {
+ outStream.Write(lzma.Properties);
+
+ long fileSize = content.Length;
+ for (int i = 0; i < 8; i++)
+ outStream.WriteByte((byte)(fileSize >> (8 * i)));
+
+ lzma.Write(content);
+ }
+
+ return outStream.ToArray();
+ }
+ }
+
+ private string replayStringContent
+ {
+ get
+ {
+ StringBuilder replayData = new StringBuilder();
+
+ if (score.Replay != null)
+ {
+ LegacyReplayFrame lastF = new LegacyReplayFrame(0, 0, 0, ReplayButtonState.None);
+
+ foreach (var f in score.Replay.Frames.OfType().Select(f => f.ToLegacy(beatmap)))
+ {
+ replayData.Append(FormattableString.Invariant($"{f.Time - lastF.Time}|{f.MouseX ?? 0}|{f.MouseY ?? 0}|{(int)f.ButtonState},"));
+ lastF = f;
+ }
+ }
+
+ replayData.AppendFormat(@"{0}|{1}|{2}|{3},", -12345, 0, 0, 0);
+ return replayData.ToString();
+ }
+ }
+
+ private string getHpGraphFormatted()
+ {
+ // todo: implement, maybe?
+ return string.Empty;
+ }
+ }
+}
diff --git a/osu.Game/Scoring/LegacyDatabasedScore.cs b/osu.Game/Scoring/LegacyDatabasedScore.cs
index 172e08e2d0..bd673eaa29 100644
--- a/osu.Game/Scoring/LegacyDatabasedScore.cs
+++ b/osu.Game/Scoring/LegacyDatabasedScore.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Scoring
var replayFilename = score.Files.First(f => f.Filename.EndsWith(".osr", StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath;
using (var stream = store.GetStream(replayFilename))
- Replay = new DatabasedLegacyScoreParser(rulesets, beatmaps).Parse(stream).Replay;
+ Replay = new DatabasedLegacyScoreDecoder(rulesets, beatmaps).Parse(stream).Replay;
}
}
}
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 249f0a932b..d5bd486e43 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -46,9 +46,9 @@ namespace osu.Game.Scoring
{
try
{
- return new DatabasedLegacyScoreParser(rulesets, beatmaps()).Parse(stream).ScoreInfo;
+ return new DatabasedLegacyScoreDecoder(rulesets, beatmaps()).Parse(stream).ScoreInfo;
}
- catch (LegacyScoreParser.BeatmapNotFoundException e)
+ catch (LegacyScoreDecoder.BeatmapNotFoundException e)
{
Logger.Log(e.Message, LoggingTarget.Information, LogLevel.Error);
return null;
diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
index 50fd127093..b08455be95 100644
--- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
+++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs
@@ -166,7 +166,7 @@ namespace osu.Game.Screens.Backgrounds
BlurAmount.ValueChanged += _ => UpdateVisuals();
}
- protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value || !ShowVideo.Value; // The background needs to be hidden in the case of it being replaced by the storyboard
+ protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value; // The background needs to be hidden in the case of it being replaced by the storyboard
protected override void UpdateVisuals()
{
diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs
index 127270f521..dcee5e83b7 100644
--- a/osu.Game/Screens/Menu/MainMenu.cs
+++ b/osu.Game/Screens/Menu/MainMenu.cs
@@ -140,7 +140,7 @@ namespace osu.Game.Screens.Menu
preloadSongSelect();
}
- [Resolved]
+ [Resolved(canBeNull: true)]
private OsuGame game { get; set; }
private void confirmAndExit()
@@ -148,7 +148,7 @@ namespace osu.Game.Screens.Menu
if (exitConfirmed) return;
exitConfirmed = true;
- game.PerformFromScreen(menu => menu.Exit());
+ game?.PerformFromScreen(menu => menu.Exit());
}
private void preloadSongSelect()
diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs
index ee8be87352..c978f4e96d 100644
--- a/osu.Game/Screens/Play/BreakOverlay.cs
+++ b/osu.Game/Screens/Play/BreakOverlay.cs
@@ -2,8 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -16,8 +14,6 @@ namespace osu.Game.Screens.Play
{
public class BreakOverlay : Container
{
- private readonly ScoreProcessor scoreProcessor;
-
///
/// The duration of the break overlay fading.
///
@@ -37,10 +33,6 @@ namespace osu.Game.Screens.Play
{
breaks = value;
- // reset index in case the new breaks list is smaller than last one
- isBreakTime.Value = false;
- CurrentBreakIndex = 0;
-
if (IsLoaded)
initializeBreaks();
}
@@ -48,27 +40,17 @@ namespace osu.Game.Screens.Play
public override bool RemoveCompletedTransforms => false;
- ///
- /// Whether the gameplay is currently in a break.
- ///
- public IBindable IsBreakTime => isBreakTime;
-
- protected int CurrentBreakIndex;
-
- private readonly BindableBool isBreakTime = new BindableBool();
-
private readonly Container remainingTimeAdjustmentBox;
private readonly Container remainingTimeBox;
private readonly RemainingTimeCounter remainingTimeCounter;
- private readonly BreakInfo info;
private readonly BreakArrows breakArrows;
- private readonly double gameplayStartTime;
- public BreakOverlay(bool letterboxing, double gameplayStartTime = 0, ScoreProcessor scoreProcessor = null)
+ public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor)
{
- this.gameplayStartTime = gameplayStartTime;
- this.scoreProcessor = scoreProcessor;
RelativeSizeAxes = Axes.Both;
+
+ BreakInfo info;
+
Child = fadeContainer = new Container
{
Alpha = 0,
@@ -119,13 +101,11 @@ namespace osu.Game.Screens.Play
}
};
- if (scoreProcessor != null) bindProcessor(scoreProcessor);
- }
-
- [BackgroundDependencyLoader(true)]
- private void load(GameplayClock clock)
- {
- if (clock != null) Clock = clock;
+ if (scoreProcessor != null)
+ {
+ info.AccuracyDisplay.Current.BindTo(scoreProcessor.Accuracy);
+ info.GradeDisplay.Current.BindTo(scoreProcessor.Rank);
+ }
}
protected override void LoadComplete()
@@ -134,42 +114,6 @@ namespace osu.Game.Screens.Play
initializeBreaks();
}
- protected override void Update()
- {
- base.Update();
- updateBreakTimeBindable();
- }
-
- private void updateBreakTimeBindable() =>
- isBreakTime.Value = getCurrentBreak()?.HasEffect == true
- || Clock.CurrentTime < gameplayStartTime
- || scoreProcessor?.HasCompleted == true;
-
- private BreakPeriod getCurrentBreak()
- {
- if (breaks?.Count > 0)
- {
- var time = Clock.CurrentTime;
-
- if (time > breaks[CurrentBreakIndex].EndTime)
- {
- while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
- CurrentBreakIndex++;
- }
- else
- {
- while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
- CurrentBreakIndex--;
- }
-
- var closest = breaks[CurrentBreakIndex];
-
- return closest.Contains(time) ? closest : null;
- }
-
- return null;
- }
-
private void initializeBreaks()
{
FinishTransforms(true);
@@ -207,11 +151,5 @@ namespace osu.Game.Screens.Play
}
}
}
-
- private void bindProcessor(ScoreProcessor processor)
- {
- info.AccuracyDisplay.Current.BindTo(processor.Accuracy);
- info.GradeDisplay.Current.BindTo(processor.Rank);
- }
}
}
diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs
new file mode 100644
index 0000000000..64262d52b5
--- /dev/null
+++ b/osu.Game/Screens/Play/BreakTracker.cs
@@ -0,0 +1,82 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps.Timing;
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Screens.Play
+{
+ public class BreakTracker : Component
+ {
+ private readonly ScoreProcessor scoreProcessor;
+
+ private readonly double gameplayStartTime;
+
+ ///
+ /// Whether the gameplay is currently in a break.
+ ///
+ public IBindable IsBreakTime => isBreakTime;
+
+ protected int CurrentBreakIndex;
+
+ private readonly BindableBool isBreakTime = new BindableBool();
+
+ private IReadOnlyList breaks;
+
+ public IReadOnlyList Breaks
+ {
+ get => breaks;
+ set
+ {
+ breaks = value;
+
+ // reset index in case the new breaks list is smaller than last one
+ isBreakTime.Value = false;
+ CurrentBreakIndex = 0;
+ }
+ }
+
+ public BreakTracker(double gameplayStartTime = 0, ScoreProcessor scoreProcessor = null)
+ {
+ this.gameplayStartTime = gameplayStartTime;
+ this.scoreProcessor = scoreProcessor;
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ isBreakTime.Value = getCurrentBreak()?.HasEffect == true
+ || Clock.CurrentTime < gameplayStartTime
+ || scoreProcessor?.HasCompleted == true;
+ }
+
+ private BreakPeriod getCurrentBreak()
+ {
+ if (breaks?.Count > 0)
+ {
+ var time = Clock.CurrentTime;
+
+ if (time > breaks[CurrentBreakIndex].EndTime)
+ {
+ while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
+ CurrentBreakIndex++;
+ }
+ else
+ {
+ while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
+ CurrentBreakIndex--;
+ }
+
+ var closest = breaks[CurrentBreakIndex];
+
+ return closest.Contains(time) ? closest : null;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs
index 0fe315fbab..eabdee95fb 100644
--- a/osu.Game/Screens/Play/DimmableStoryboard.cs
+++ b/osu.Game/Screens/Play/DimmableStoryboard.cs
@@ -44,7 +44,6 @@ namespace osu.Game.Screens.Play
return;
drawableStoryboard = storyboard.CreateDrawable();
- drawableStoryboard.Masking = true;
if (async)
LoadComponentAsync(drawableStoryboard, Add);
diff --git a/osu.Game/Screens/Play/DimmableVideo.cs b/osu.Game/Screens/Play/DimmableVideo.cs
deleted file mode 100644
index 1a01cace17..0000000000
--- a/osu.Game/Screens/Play/DimmableVideo.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Video;
-using osu.Game.Graphics.Containers;
-using osuTK.Graphics;
-
-namespace osu.Game.Screens.Play
-{
- public class DimmableVideo : UserDimContainer
- {
- private readonly VideoSprite video;
- private DrawableVideo drawableVideo;
-
- public DimmableVideo(VideoSprite video)
- {
- this.video = video;
- }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- initializeVideo(false);
- }
-
- protected override void LoadComplete()
- {
- ShowVideo.BindValueChanged(_ => initializeVideo(true), true);
- base.LoadComplete();
- }
-
- protected override bool ShowDimContent => IgnoreUserSettings.Value || (ShowVideo.Value && DimLevel < 1);
-
- private void initializeVideo(bool async)
- {
- if (video == null)
- return;
-
- if (drawableVideo != null)
- return;
-
- if (!ShowVideo.Value && !IgnoreUserSettings.Value)
- return;
-
- drawableVideo = new DrawableVideo(video);
-
- if (async)
- LoadComponentAsync(drawableVideo, Add);
- else
- Add(drawableVideo);
- }
-
- private class DrawableVideo : Container
- {
- public DrawableVideo(VideoSprite video)
- {
- RelativeSizeAxes = Axes.Both;
- Masking = true;
-
- video.RelativeSizeAxes = Axes.Both;
- video.FillMode = FillMode.Fit;
- video.Anchor = Anchor.Centre;
- video.Origin = Anchor.Centre;
-
- AddRangeInternal(new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black,
- },
- video,
- });
- }
-
- [BackgroundDependencyLoader]
- private void load(GameplayClock clock)
- {
- if (clock != null)
- Clock = clock;
- }
- }
- }
-}
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index a120963abd..63ec3b0d2d 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@@ -17,13 +18,16 @@ using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.Containers;
+using osu.Game.IO.Archives;
using osu.Game.Online.API;
using osu.Game.Overlays;
+using osu.Game.Replays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
+using osu.Game.Scoring.Legacy;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
using osu.Game.Users;
@@ -72,6 +76,8 @@ namespace osu.Game.Screens.Play
public BreakOverlay BreakOverlay;
+ private BreakTracker breakTracker;
+
protected ScoreProcessor ScoreProcessor { get; private set; }
protected HealthProcessor HealthProcessor { get; private set; }
@@ -85,7 +91,6 @@ namespace osu.Game.Screens.Play
protected GameplayClockContainer GameplayClockContainer { get; private set; }
public DimmableStoryboard DimmableStoryboard { get; private set; }
- public DimmableVideo DimmableVideo { get; private set; }
[Cached]
[Cached(Type = typeof(IBindable>))]
@@ -118,6 +123,23 @@ namespace osu.Game.Screens.Play
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ PrepareReplay();
+ }
+
+ private Replay recordingReplay;
+
+ ///
+ /// Run any recording / playback setup for replays.
+ ///
+ protected virtual void PrepareReplay()
+ {
+ DrawableRuleset.SetRecordTarget(recordingReplay = new Replay());
+ }
+
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuConfigManager config)
{
@@ -184,12 +206,11 @@ namespace osu.Game.Screens.Play
foreach (var mod in Mods.Value.OfType())
mod.ApplyToHealthProcessor(HealthProcessor);
- BreakOverlay.IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
+ breakTracker.IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
}
private void addUnderlayComponents(Container target)
{
- target.Add(DimmableVideo = new DimmableVideo(Beatmap.Value.Video) { RelativeSizeAxes = Axes.Both });
target.Add(DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both });
}
@@ -212,12 +233,30 @@ namespace osu.Game.Screens.Play
DrawableRuleset,
new ComboEffects(ScoreProcessor)
});
+
+ DrawableRuleset.FrameStableComponents.AddRange(new Drawable[]
+ {
+ ScoreProcessor,
+ HealthProcessor,
+ breakTracker = new BreakTracker(DrawableRuleset.GameplayStartTime, ScoreProcessor)
+ {
+ Breaks = working.Beatmap.Breaks
+ }
+ });
+
+ HealthProcessor.IsBreakTime.BindTo(breakTracker.IsBreakTime);
}
private void addOverlayComponents(Container target, WorkingBeatmap working)
{
target.AddRange(new[]
{
+ BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
+ {
+ Clock = DrawableRuleset.FrameStableClock,
+ ProcessCustomClock = false,
+ Breaks = working.Beatmap.Breaks
+ },
// display the cursor above some HUD elements.
DrawableRuleset.Cursor?.CreateProxy() ?? new Container(),
DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(),
@@ -274,20 +313,8 @@ namespace osu.Game.Screens.Play
performImmediateExit();
},
},
- failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }
+ failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, },
});
-
- DrawableRuleset.Overlays.Add(BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, DrawableRuleset.GameplayStartTime, ScoreProcessor)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Breaks = working.Beatmap.Breaks
- });
-
- DrawableRuleset.Overlays.Add(ScoreProcessor);
- DrawableRuleset.Overlays.Add(HealthProcessor);
-
- HealthProcessor.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
}
private void onBreakTimeChanged(ValueChangedEvent isBreakTime)
@@ -299,7 +326,7 @@ namespace osu.Game.Screens.Play
private void updatePauseOnFocusLostState() =>
HUDOverlay.HoldToQuit.PauseOnFocusLost = PauseOnFocusLost
&& !DrawableRuleset.HasReplayLoaded.Value
- && !BreakOverlay.IsBreakTime.Value;
+ && !breakTracker.IsBreakTime.Value;
private IBeatmap loadPlayableBeatmap()
{
@@ -521,7 +548,7 @@ namespace osu.Game.Screens.Play
PauseOverlay.Hide();
// breaks and time-based conditions may allow instant resume.
- if (BreakOverlay.IsBreakTime.Value)
+ if (breakTracker.IsBreakTime.Value)
completeResume();
else
DrawableRuleset.RequestResume(completeResume);
@@ -555,9 +582,8 @@ namespace osu.Game.Screens.Play
Background.BlurAmount.Value = 0;
// bind component bindables.
- Background.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
- DimmableStoryboard.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
- DimmableVideo.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
+ Background.IsBreakTime.BindTo(breakTracker.IsBreakTime);
+ DimmableStoryboard.IsBreakTime.BindTo(breakTracker.IsBreakTime);
Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
@@ -625,19 +651,33 @@ namespace osu.Game.Screens.Play
completionProgressDelegate?.Cancel();
completionProgressDelegate = Schedule(delegate
{
- var score = CreateScore();
-
- if (DrawableRuleset.ReplayScore == null)
- {
- scoreManager.Import(score).ContinueWith(_ => Schedule(() =>
- {
- // screen may be in the exiting transition phase.
- if (this.IsCurrentScreen())
- this.Push(CreateResults(score));
- }));
- }
+ if (DrawableRuleset.ReplayScore != null)
+ this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo));
else
- this.Push(CreateResults(score));
+ {
+ var score = new Score { ScoreInfo = CreateScore() };
+
+ LegacyByteArrayReader replayReader = null;
+
+ if (recordingReplay?.Frames.Count > 0)
+ {
+ score.Replay = recordingReplay;
+
+ using (var stream = new MemoryStream())
+ {
+ new LegacyScoreEncoder(score, gameplayBeatmap.PlayableBeatmap).Encode(stream);
+ replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr");
+ }
+ }
+
+ scoreManager.Import(score.ScoreInfo, replayReader)
+ .ContinueWith(imported => Schedule(() =>
+ {
+ // screen may be in the exiting transition phase.
+ if (this.IsCurrentScreen())
+ this.Push(CreateResults(imported.Result));
+ }));
+ }
});
}
diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
index 9db3a587fa..d6c66d0751 100644
--- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
+++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
@@ -15,7 +15,6 @@ namespace osu.Game.Screens.Play.PlayerSettings
private readonly PlayerSliderBar dimSliderBar;
private readonly PlayerSliderBar blurSliderBar;
private readonly PlayerCheckbox showStoryboardToggle;
- private readonly PlayerCheckbox showVideoToggle;
private readonly PlayerCheckbox beatmapSkinsToggle;
private readonly PlayerCheckbox beatmapHitsoundsToggle;
@@ -43,8 +42,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
{
Text = "Toggles:"
},
- showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" },
- showVideoToggle = new PlayerCheckbox { LabelText = "Video" },
+ showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboard / Video" },
beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" },
beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" }
};
@@ -56,7 +54,6 @@ namespace osu.Game.Screens.Play.PlayerSettings
dimSliderBar.Bindable = config.GetBindable(OsuSetting.DimLevel);
blurSliderBar.Bindable = config.GetBindable(OsuSetting.BlurLevel);
showStoryboardToggle.Current = config.GetBindable(OsuSetting.ShowStoryboard);
- showVideoToggle.Current = config.GetBindable(OsuSetting.ShowVideoBackground);
beatmapSkinsToggle.Current = config.GetBindable(OsuSetting.BeatmapSkins);
beatmapHitsoundsToggle.Current = config.GetBindable(OsuSetting.BeatmapHitsounds);
}
diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs
index b040549efc..8708b5f634 100644
--- a/osu.Game/Screens/Play/ReplayPlayer.cs
+++ b/osu.Game/Screens/Play/ReplayPlayer.cs
@@ -18,9 +18,8 @@ namespace osu.Game.Screens.Play
this.score = score;
}
- protected override void LoadComplete()
+ protected override void PrepareReplay()
{
- base.LoadComplete();
DrawableRuleset?.SetReplayScore(score);
}
diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs
index 6a03cfb68e..d613ce649a 100644
--- a/osu.Game/Screens/Select/FilterControl.cs
+++ b/osu.Game/Screens/Select/FilterControl.cs
@@ -16,6 +16,7 @@ using Container = osu.Framework.Graphics.Containers.Container;
using osu.Framework.Graphics.Shapes;
using osu.Game.Configuration;
using osu.Game.Rulesets;
+using osu.Framework.Input.Events;
namespace osu.Game.Screens.Select
{
@@ -136,6 +137,8 @@ namespace osu.Game.Screens.Select
public void Deactivate()
{
+ searchTextBox.ReadOnly = true;
+
searchTextBox.HoldFocus = false;
if (searchTextBox.HasFocus)
GetContainingInputManager().ChangeFocus(searchTextBox);
@@ -143,6 +146,7 @@ namespace osu.Game.Screens.Select
public void Activate()
{
+ searchTextBox.ReadOnly = false;
searchTextBox.HoldFocus = true;
}
@@ -184,5 +188,9 @@ namespace osu.Game.Screens.Select
}
private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
+
+ protected override bool OnClick(ClickEvent e) => true;
+
+ protected override bool OnHover(HoverEvent e) => true;
}
}
diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs
index 1dc7081c1c..689a11166a 100644
--- a/osu.Game/Screens/Select/Footer.cs
+++ b/osu.Game/Screens/Select/Footer.cs
@@ -107,5 +107,7 @@ namespace osu.Game.Screens.Select
protected override bool OnMouseDown(MouseDownEvent e) => true;
protected override bool OnClick(ClickEvent e) => true;
+
+ protected override bool OnHover(HoverEvent e) => true;
}
}
diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs
index 9e2f5761dd..179aab54a3 100644
--- a/osu.Game/Screens/Select/PlaySongSelect.cs
+++ b/osu.Game/Screens/Select/PlaySongSelect.cs
@@ -56,6 +56,7 @@ namespace osu.Game.Screens.Select
switch (e.Key)
{
case Key.Enter:
+ case Key.KeypadEnter:
// this is a special hard-coded case; we can't rely on OnPressed (of SongSelect) as GlobalActionContainer is
// matching with exact modifier consideration (so Ctrl+Enter would be ignored).
FinaliseSelection();
@@ -84,8 +85,6 @@ namespace osu.Game.Screens.Select
}
}
- Beatmap.Value.Track.Looping = false;
-
SampleConfirm?.Play();
this.Push(player = new PlayerLoader(() => new Player()));
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index b6ec40ab88..895a8ad0c9 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -572,6 +572,9 @@ namespace osu.Game.Screens.Select
BeatmapOptions.Hide();
+ if (Beatmap.Value.Track != null)
+ Beatmap.Value.Track.Looping = false;
+
this.ScaleTo(1.1f, 250, Easing.InSine);
this.FadeOut(250);
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
index 94d7395ecf..bc6e01a729 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
@@ -75,7 +75,7 @@ namespace osu.Game.Storyboards.Drawables
private void updateLayerVisibility()
{
foreach (var layer in Children)
- layer.Enabled = passing ? layer.Layer.EnabledWhenPassing : layer.Layer.EnabledWhenFailing;
+ layer.Enabled = passing ? layer.Layer.VisibleWhenPassing : layer.Layer.VisibleWhenFailing;
}
}
}
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
index 39f5418902..def4eed2ca 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
@@ -21,7 +21,8 @@ namespace osu.Game.Storyboards.Drawables
RelativeSizeAxes = Axes.Both;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- Enabled = layer.EnabledWhenPassing;
+ Enabled = layer.VisibleWhenPassing;
+ Masking = layer.Masking;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs
new file mode 100644
index 0000000000..d4dbdf1ea8
--- /dev/null
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs
@@ -0,0 +1,64 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.Graphics.Video;
+using osu.Framework.Timing;
+using osu.Game.Beatmaps;
+
+namespace osu.Game.Storyboards.Drawables
+{
+ public class DrawableStoryboardVideo : CompositeDrawable
+ {
+ public readonly StoryboardVideo Video;
+ private VideoSprite videoSprite;
+
+ public override bool RemoveWhenNotAlive => false;
+
+ public DrawableStoryboardVideo(StoryboardVideo video)
+ {
+ Video = video;
+
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader(true)]
+ private void load(IBindable beatmap, TextureStore textureStore)
+ {
+ var path = beatmap.Value.BeatmapSetInfo?.Files?.Find(f => f.Filename.Equals(Video.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath;
+
+ if (path == null)
+ return;
+
+ var stream = textureStore.GetStream(path);
+
+ if (stream == null)
+ return;
+
+ InternalChild = videoSprite = new VideoSprite(stream, false)
+ {
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fill,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Alpha = 0,
+ Clock = new FramedOffsetClock(Clock) { Offset = -Video.StartTime }
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ if (videoSprite == null) return;
+
+ using (videoSprite.BeginAbsoluteSequence(0))
+ videoSprite.FadeIn(500);
+ }
+ }
+}
diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs
index 35bfe8c229..a1ddafbacf 100644
--- a/osu.Game/Storyboards/Storyboard.cs
+++ b/osu.Game/Storyboards/Storyboard.cs
@@ -1,10 +1,10 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Game.Beatmaps;
-using osu.Game.Storyboards.Drawables;
using System.Collections.Generic;
using System.Linq;
+using osu.Game.Beatmaps;
+using osu.Game.Storyboards.Drawables;
namespace osu.Game.Storyboards
{
@@ -21,9 +21,10 @@ namespace osu.Game.Storyboards
public Storyboard()
{
+ layers.Add("Video", new StoryboardLayer("Video", 4, false));
layers.Add("Background", new StoryboardLayer("Background", 3));
- layers.Add("Fail", new StoryboardLayer("Fail", 2) { EnabledWhenPassing = false, });
- layers.Add("Pass", new StoryboardLayer("Pass", 1) { EnabledWhenFailing = false, });
+ layers.Add("Fail", new StoryboardLayer("Fail", 2) { VisibleWhenPassing = false, });
+ layers.Add("Pass", new StoryboardLayer("Pass", 1) { VisibleWhenFailing = false, });
layers.Add("Foreground", new StoryboardLayer("Foreground", 0));
}
@@ -46,6 +47,9 @@ namespace osu.Game.Storyboards
if (backgroundPath == null)
return false;
+ if (GetLayer("Video").Elements.Any())
+ return true;
+
return GetLayer("Background").Elements.Any(e => e.Path.ToLowerInvariant() == backgroundPath);
}
}
diff --git a/osu.Game/Storyboards/StoryboardLayer.cs b/osu.Game/Storyboards/StoryboardLayer.cs
index d15f771534..142bc60deb 100644
--- a/osu.Game/Storyboards/StoryboardLayer.cs
+++ b/osu.Game/Storyboards/StoryboardLayer.cs
@@ -8,17 +8,23 @@ namespace osu.Game.Storyboards
{
public class StoryboardLayer
{
- public string Name;
- public int Depth;
- public bool EnabledWhenPassing = true;
- public bool EnabledWhenFailing = true;
+ public readonly string Name;
+
+ public readonly int Depth;
+
+ public readonly bool Masking;
+
+ public bool VisibleWhenPassing = true;
+
+ public bool VisibleWhenFailing = true;
public List Elements = new List();
- public StoryboardLayer(string name, int depth)
+ public StoryboardLayer(string name, int depth, bool masking = true)
{
Name = name;
Depth = depth;
+ Masking = masking;
}
public void Add(IStoryboardElement element)
diff --git a/osu.Game/Storyboards/StoryboardVideo.cs b/osu.Game/Storyboards/StoryboardVideo.cs
new file mode 100644
index 0000000000..4652e45852
--- /dev/null
+++ b/osu.Game/Storyboards/StoryboardVideo.cs
@@ -0,0 +1,25 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics;
+using osu.Game.Storyboards.Drawables;
+
+namespace osu.Game.Storyboards
+{
+ public class StoryboardVideo : IStoryboardElement
+ {
+ public string Path { get; }
+
+ public bool IsDrawable => true;
+
+ public double StartTime { get; }
+
+ public StoryboardVideo(string path, int offset)
+ {
+ Path = path;
+ StartTime = offset;
+ }
+
+ public Drawable CreateDrawable() => new DrawableStoryboardVideo(this);
+ }
+}
diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
index ef86186e41..b60add6e3b 100644
--- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
+++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
@@ -10,7 +10,6 @@ using Newtonsoft.Json;
using NUnit.Framework;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
@@ -207,8 +206,6 @@ namespace osu.Game.Tests.Beatmaps
protected override Texture GetBackground() => throw new NotImplementedException();
- protected override VideoSprite GetVideo() => throw new NotImplementedException();
-
protected override Track GetTrack() => throw new NotImplementedException();
protected override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
diff --git a/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs b/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs
index e9251f8011..e93bf916c7 100644
--- a/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs
+++ b/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Tests.Beatmaps
protected void Test(LegacyMods legacyMods, Type[] expectedMods)
{
var ruleset = CreateRuleset();
- var mods = ruleset.ConvertLegacyMods(legacyMods).ToList();
+ var mods = ruleset.ConvertFromLegacyMods(legacyMods).ToList();
Assert.AreEqual(expectedMods.Length, mods.Count);
foreach (var modType in expectedMods)
diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs
index 871d8ee3f1..6db34af20c 100644
--- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs
+++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs
@@ -3,7 +3,6 @@
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Video;
using osu.Game.Beatmaps;
using osu.Game.Storyboards;
@@ -32,8 +31,6 @@ namespace osu.Game.Tests.Beatmaps
protected override Texture GetBackground() => null;
- protected override VideoSprite GetVideo() => null;
-
protected override Track GetTrack() => null;
}
}
diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs
index d5e6d5f13e..6f59f9e443 100644
--- a/osu.Game/Users/UserPanel.cs
+++ b/osu.Game/Users/UserPanel.cs
@@ -36,9 +36,10 @@ namespace osu.Game.Users
protected DelayedLoadUnloadWrapper Background { get; private set; }
+ protected TextFlowContainer LastVisitMessage { get; private set; }
+
private SpriteIcon statusIcon;
private OsuSpriteText statusMessage;
- private TextFlowContainer lastVisitMessage;
protected UserPanel(User user)
{
@@ -153,7 +154,7 @@ namespace osu.Game.Users
var alignment = rightAlignedChildren ? Anchor.CentreRight : Anchor.CentreLeft;
- statusContainer.Add(lastVisitMessage = new TextFlowContainer(t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)).With(text =>
+ statusContainer.Add(LastVisitMessage = new TextFlowContainer(t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)).With(text =>
{
text.Anchor = alignment;
text.Origin = alignment;
@@ -184,6 +185,8 @@ namespace osu.Game.Users
{
if (status != null)
{
+ LastVisitMessage.FadeTo(status is UserStatusOffline && User.LastVisit.HasValue ? 1 : 0);
+
// Set status message based on activity (if we have one) and status is not offline
if (activity != null && !(status is UserStatusOffline))
{
@@ -193,7 +196,6 @@ namespace osu.Game.Users
}
// Otherwise use only status
- lastVisitMessage.FadeTo(status is UserStatusOffline && User.LastVisit.HasValue ? 1 : 0);
statusMessage.Text = status.Message;
statusIcon.FadeColour(status.GetAppropriateColour(colours), 500, Easing.OutQuint);
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 3894c06994..781c566b5f 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -23,7 +23,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 9cc9792ecf..a2c6106931 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -71,7 +71,7 @@
-
+
@@ -79,7 +79,7 @@
-
+