mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:33:30 +08:00
Merge branch 'master' into show-mod-settings-tooltip
This commit is contained in:
commit
2abb8a37df
@ -52,6 +52,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.319.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.327.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
new KeyBinding(InputKey.Shift, CatchAction.Dash),
|
||||
};
|
||||
|
||||
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
|
||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||
{
|
||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
||||
yield return new CatchModNightcore();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs
Normal file
26
osu.Game.Rulesets.Catch/UI/CatchReplayRecorder.cs
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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<CatchAction>
|
||||
{
|
||||
private readonly CatchPlayfield playfield;
|
||||
|
||||
public CatchReplayRecorder(Replay target, CatchPlayfield playfield)
|
||||
: base(target)
|
||||
{
|
||||
this.playfield = playfield;
|
||||
}
|
||||
|
||||
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<CatchAction> actions, ReplayFrame previousFrame)
|
||||
=> new CatchReplayFrame(Time.Current, playfield.CatcherArea.MovableCatcher.X, actions.Contains(CatchAction.Dash), previousFrame as CatchReplayFrame);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
}
|
||||
|
||||
protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
protected override KeyBindingContainer<ManiaAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
=> new LocalKeyBindingContainer(ruleset, variant, unique);
|
||||
|
||||
private class LocalKeyBindingContainer : RulesetKeyBindingContainer
|
||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new ManiaLegacySkinTransformer(source);
|
||||
|
||||
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
|
||||
public override IEnumerable<Mod> 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<Mod> GetModsFor(ModType type)
|
||||
{
|
||||
switch (type)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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<int>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
23
osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs
Normal file
23
osu.Game.Rulesets.Mania/UI/ManiaReplayRecorder.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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<ManiaAction>
|
||||
{
|
||||
public ManiaReplayRecorder(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<ManiaAction> actions, ReplayFrame previousFrame)
|
||||
=> new ManiaReplayFrame(Time.Current, actions.ToArray());
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
/// </summary>
|
||||
public bool AllowUserCursorMovement { get; set; } = true;
|
||||
|
||||
protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
protected override KeyBindingContainer<OsuAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
=> new OsuKeyBindingContainer(ruleset, variant, unique);
|
||||
|
||||
public OsuInputManager(RulesetInfo ruleset)
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
|
||||
};
|
||||
|
||||
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
|
||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||
{
|
||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
||||
yield return new OsuModNightcore();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
23
osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs
Normal file
23
osu.Game.Rulesets.Osu/UI/OsuReplayRecorder.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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<OsuAction>
|
||||
{
|
||||
public OsuReplayRecorder(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<OsuAction> actions, ReplayFrame previousFrame)
|
||||
=> new OsuReplayFrame(Time.Current, mousePosition, actions.ToArray());
|
||||
}
|
||||
}
|
27
osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs
Normal file
27
osu.Game.Rulesets.Taiko/Mods/TaikoModRandom.cs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Taiko
|
||||
new KeyBinding(InputKey.K, TaikoAction.RightRim),
|
||||
};
|
||||
|
||||
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
|
||||
public override IEnumerable<Mod> 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(),
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
23
osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs
Normal file
23
osu.Game.Rulesets.Taiko/UI/TaikoReplayRecorder.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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<TaikoAction>
|
||||
{
|
||||
public TaikoReplayRecorder(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<TaikoAction> actions, ReplayFrame previousFrame) =>
|
||||
new TaikoReplayFrame(Time.Current, actions.ToArray());
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
282
osu.Game.Tests/Gameplay/TestSceneReplayRecorder.cs
Normal file
282
osu.Game.Tests/Gameplay/TestSceneReplayRecorder.cs
Normal file
@ -0,0 +1,282 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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<Box>().First().Position == recordingManager.ChildrenOfType<Box>().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<TestReplayFrame>
|
||||
{
|
||||
public TestFramedReplayInputHandler(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
public override List<IInput> GetPendingInputs()
|
||||
{
|
||||
return new List<IInput>
|
||||
{
|
||||
new MousePositionAbsoluteInput
|
||||
{
|
||||
Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero)
|
||||
},
|
||||
new ReplayState<TestAction>
|
||||
{
|
||||
PressedActions = CurrentFrame?.Actions ?? new List<TestAction>()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class TestInputConsumer : CompositeDrawable, IKeyBindingHandler<TestAction>
|
||||
{
|
||||
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<TestAction>
|
||||
{
|
||||
public TestRulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
: base(ruleset, variant, unique)
|
||||
{
|
||||
}
|
||||
|
||||
protected override KeyBindingContainer<TestAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
=> new TestKeyBindingContainer();
|
||||
|
||||
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
|
||||
{
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class TestReplayFrame : ReplayFrame
|
||||
{
|
||||
public Vector2 Position;
|
||||
|
||||
public List<TestAction> Actions = new List<TestAction>();
|
||||
|
||||
public TestReplayFrame(double time, Vector2 position, params TestAction[] actions)
|
||||
: base(time)
|
||||
{
|
||||
Position = position;
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TestAction
|
||||
{
|
||||
Down,
|
||||
}
|
||||
|
||||
internal class TestReplayRecorder : ReplayRecorder<TestAction>
|
||||
{
|
||||
public TestReplayRecorder(Replay target)
|
||||
: base(target)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<TestAction> actions, ReplayFrame previousFrame)
|
||||
=> new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
221
osu.Game.Tests/Gameplay/TestSceneReplayRecording.cs
Normal file
221
osu.Game.Tests/Gameplay/TestSceneReplayRecording.cs
Normal file
@ -0,0 +1,221 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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<TestReplayFrame>
|
||||
{
|
||||
public TestFramedReplayInputHandler(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
public override List<IInput> GetPendingInputs()
|
||||
{
|
||||
return new List<IInput>
|
||||
{
|
||||
new MousePositionAbsoluteInput
|
||||
{
|
||||
Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero)
|
||||
},
|
||||
new ReplayState<TestAction>
|
||||
{
|
||||
PressedActions = CurrentFrame?.Actions ?? new List<TestAction>()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class TestConsumer : CompositeDrawable, IKeyBindingHandler<TestAction>
|
||||
{
|
||||
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<TestAction>
|
||||
{
|
||||
public TestRulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
: base(ruleset, variant, unique)
|
||||
{
|
||||
}
|
||||
|
||||
protected override KeyBindingContainer<TestAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
=> new TestKeyBindingContainer();
|
||||
|
||||
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
|
||||
{
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class TestReplayFrame : ReplayFrame
|
||||
{
|
||||
public Vector2 Position;
|
||||
|
||||
public List<TestAction> Actions = new List<TestAction>();
|
||||
|
||||
public TestReplayFrame(double time, Vector2 position, params TestAction[] actions)
|
||||
: base(time)
|
||||
{
|
||||
Position = position;
|
||||
Actions.AddRange(actions);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TestAction
|
||||
{
|
||||
Down,
|
||||
}
|
||||
|
||||
internal class TestReplayRecorder : ReplayRecorder<TestAction>
|
||||
{
|
||||
public TestReplayRecorder(Replay target)
|
||||
: base(target)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<TestAction> actions, ReplayFrame previousFrame) =>
|
||||
new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
|
||||
}
|
||||
}
|
31
osu.Game.Tests/Resources/storyboard_no_video.osu
Normal file
31
osu.Game.Tests/Resources/storyboard_no_video.osu
Normal file
@ -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:
|
@ -236,8 +236,6 @@ namespace osu.Game.Tests.Scores.IO
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Filenames => new[] { "test_file.osr" };
|
||||
|
||||
public override Stream GetUnderlyingStream() => new MemoryStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<BreakTracker>().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<BreakInfo>().Single().AccuracyDisplay.Current.Value == 1);
|
||||
AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
|
||||
AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
|
||||
}
|
||||
|
@ -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<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(BreakOverlay),
|
||||
};
|
||||
|
||||
private readonly TestBreakOverlay breakOverlay;
|
||||
private readonly BreakOverlay breakOverlay;
|
||||
|
||||
private readonly TestBreakTracker breakTracker;
|
||||
|
||||
private readonly IReadOnlyList<BreakPeriod> testBreaks = new List<BreakPeriod>
|
||||
{
|
||||
@ -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<BreakPeriod>
|
||||
AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = breakTracker.Breaks = new List<BreakPeriod>
|
||||
{
|
||||
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<BreakPeriod> 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()
|
||||
{
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
{
|
||||
|
87
osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs
Normal file
87
osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Overlays.Dashboard.Friends;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Framework.Allocation;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneFriendDisplay : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(FriendDisplay),
|
||||
typeof(FriendOnlineStreamControl),
|
||||
typeof(UserListToolbar)
|
||||
};
|
||||
|
||||
protected override bool UseOnlineAPI => true;
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||
|
||||
private FriendDisplay display;
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
Child = new BasicScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = display = new FriendDisplay()
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestOffline()
|
||||
{
|
||||
AddStep("Populate", () => display.Users = getUsers());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOnline()
|
||||
{
|
||||
AddStep("Fetch online", () => display?.Fetch());
|
||||
}
|
||||
|
||||
private List<User> getUsers() => new List<User>
|
||||
{
|
||||
new User
|
||||
{
|
||||
Username = "flyte",
|
||||
Id = 3103765,
|
||||
IsOnline = true,
|
||||
CurrentModeRank = 1111,
|
||||
Country = new Country { FlagName = "JP" },
|
||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
||||
},
|
||||
new User
|
||||
{
|
||||
Username = "peppy",
|
||||
Id = 2,
|
||||
IsOnline = false,
|
||||
CurrentModeRank = 2222,
|
||||
Country = new Country { FlagName = "AU" },
|
||||
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||
IsSupporter = true,
|
||||
SupportLevel = 3,
|
||||
},
|
||||
new User
|
||||
{
|
||||
Username = "Evast",
|
||||
Id = 8195163,
|
||||
Country = new Country { FlagName = "BY" },
|
||||
CoverUrl = "https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
|
||||
IsOnline = false,
|
||||
LastVisit = DateTimeOffset.Now
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
private readonly Bindable<UserStatus> status = new Bindable<UserStatus>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Home.Friends;
|
||||
using osu.Game.Overlays.Dashboard.Friends;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
@ -17,20 +17,20 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(FriendsOnlineStatusControl),
|
||||
typeof(FriendOnlineStreamControl),
|
||||
typeof(FriendsOnlineStatusItem),
|
||||
typeof(OverlayStreamControl<>),
|
||||
typeof(OverlayStreamItem<>),
|
||||
typeof(FriendsBundle)
|
||||
typeof(FriendStream)
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
private FriendsOnlineStatusControl control;
|
||||
private FriendOnlineStreamControl control;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() => Child = control = new FriendsOnlineStatusControl
|
||||
public void SetUp() => Schedule(() => Child = control = new FriendOnlineStreamControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -55,9 +55,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
}
|
||||
}));
|
||||
|
||||
AddAssert("3 users", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.All)?.Count == 3);
|
||||
AddAssert("1 online user", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.Online)?.Count == 1);
|
||||
AddAssert("2 offline users", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.Offline)?.Count == 2);
|
||||
AddAssert("3 users", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.All)?.Count == 3);
|
||||
AddAssert("1 online user", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.Online)?.Count == 1);
|
||||
AddAssert("2 offline users", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.Offline)?.Count == 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Home.Friends;
|
||||
using osu.Game.Overlays.Dashboard.Friends;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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]));
|
||||
|
||||
|
@ -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}"));
|
||||
}
|
||||
|
@ -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]);
|
||||
|
@ -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
|
||||
/// </summary>
|
||||
Texture Background { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the video background file for this <see cref="WorkingBeatmap"/>.
|
||||
/// </summary>
|
||||
VideoSprite Video { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the audio track for this <see cref="WorkingBeatmap"/>.
|
||||
/// </summary>
|
||||
|
@ -8,6 +8,7 @@ namespace osu.Game.Beatmaps.Legacy
|
||||
Background = 0,
|
||||
Fail = 1,
|
||||
Pass = 2,
|
||||
Foreground = 3
|
||||
Foreground = 3,
|
||||
Video = 4
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,22 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.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<Texture> background;
|
||||
|
||||
public VideoSprite Video => GetVideo();
|
||||
|
||||
protected abstract VideoSprite GetVideo();
|
||||
|
||||
public bool TrackLoaded => track.IsResultAvailable;
|
||||
public Track Track => track.Value;
|
||||
protected abstract Track GetTrack();
|
||||
|
@ -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,
|
||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
/// <summary>
|
||||
/// Whether player is in break time.
|
||||
/// Must be bound to <see cref="BreakOverlay.IsBreakTime"/> to allow for dim adjustments in gameplay.
|
||||
/// Must be bound to <see cref="BreakTracker.IsBreakTime"/> to allow for dim adjustments in gameplay.
|
||||
/// </summary>
|
||||
public readonly IBindable<bool> IsBreakTime = new Bindable<bool>();
|
||||
|
||||
@ -55,8 +55,6 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
protected Bindable<bool> ShowStoryboard { get; private set; }
|
||||
|
||||
protected Bindable<bool> 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<double>(OsuSetting.DimLevel);
|
||||
LightenDuringBreaks = config.GetBindable<bool>(OsuSetting.LightenDuringBreaks);
|
||||
ShowStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||
ShowVideo = config.GetBindable<bool>(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();
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.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
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets <see cref="SpriteText.Text">Text</see> to a new value after a duration.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||
public static TransformSequence<T> TransformTextTo<T>(this T spriteText, string newText, double duration = 0, Easing easing = Easing.None)
|
||||
where T : OsuSpriteText
|
||||
=> spriteText.TransformTo(nameof(OsuSpriteText.Text), newText, duration, easing);
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="SpriteText.Text">Text</see> to a new value after a duration.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||
public static TransformSequence<T> TransformTextTo<T>(this TransformSequence<T> t, string newText, double duration = 0, Easing easing = Easing.None)
|
||||
where T : OsuSpriteText
|
||||
=> t.Append(o => o.TransformTextTo(newText, duration, easing));
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,5 @@ namespace osu.Game.IO.Archives
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Stream GetUnderlyingStream();
|
||||
}
|
||||
}
|
||||
|
30
osu.Game/IO/Archives/LegacyByteArrayReader.cs
Normal file
30
osu.Game/IO/Archives/LegacyByteArrayReader.cs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace osu.Game.IO.Archives
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows reading a single file from the provided stream.
|
||||
/// </summary>
|
||||
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<string> Filenames => new[] { Name };
|
||||
}
|
||||
}
|
@ -28,7 +28,5 @@ namespace osu.Game.IO.Archives
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Filenames => Directory.GetFiles(path, "*", SearchOption.AllDirectories).Select(f => f.Replace(path, string.Empty).Trim(Path.DirectorySeparatorChar)).ToArray();
|
||||
|
||||
public override Stream GetUnderlyingStream() => null;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,5 @@ namespace osu.Game.IO.Archives
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Filenames => new[] { Name };
|
||||
|
||||
public override Stream GetUnderlyingStream() => null;
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,5 @@ namespace osu.Game.IO.Archives
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Filenames => archive.Entries.Select(e => e.Key).ExcludeSystemFileNames();
|
||||
|
||||
public override Stream GetUnderlyingStream() => archiveStream;
|
||||
}
|
||||
}
|
||||
|
267
osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs
Normal file
267
osu.Game/Overlays/Dashboard/Friends/FriendDisplay.cs
Normal file
@ -0,0 +1,267 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
public class FriendDisplay : CompositeDrawable
|
||||
{
|
||||
private List<User> users = new List<User>();
|
||||
|
||||
public List<User> Users
|
||||
{
|
||||
get => users;
|
||||
set
|
||||
{
|
||||
users = value;
|
||||
|
||||
onlineStreamControl.Populate(value);
|
||||
}
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
private GetFriendsRequest request;
|
||||
private CancellationTokenSource cancellationToken;
|
||||
|
||||
private Drawable currentContent;
|
||||
|
||||
private readonly FriendOnlineStreamControl onlineStreamControl;
|
||||
private readonly Box background;
|
||||
private readonly Box controlBackground;
|
||||
private readonly UserListToolbar userListToolbar;
|
||||
private readonly Container itemsPlaceholder;
|
||||
private readonly LoadingLayer loading;
|
||||
|
||||
public FriendDisplay()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
controlBackground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Top = 20,
|
||||
Horizontal = 45
|
||||
},
|
||||
Child = onlineStreamControl = new FriendOnlineStreamControl(),
|
||||
}
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = "User List",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Margin = new MarginPadding { Bottom = 20 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = 40,
|
||||
Vertical = 20
|
||||
},
|
||||
Child = userListToolbar = new UserListToolbar
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
itemsPlaceholder = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Horizontal = 50 }
|
||||
},
|
||||
loading = new LoadingLayer(itemsPlaceholder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
background.Colour = colourProvider.Background4;
|
||||
controlBackground.Colour = colourProvider.Background5;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
onlineStreamControl.Current.BindValueChanged(_ => recreatePanels());
|
||||
userListToolbar.DisplayStyle.BindValueChanged(_ => recreatePanels());
|
||||
userListToolbar.SortCriteria.BindValueChanged(_ => recreatePanels());
|
||||
}
|
||||
|
||||
public void Fetch()
|
||||
{
|
||||
if (!api.IsLoggedIn)
|
||||
return;
|
||||
|
||||
request = new GetFriendsRequest();
|
||||
request.Success += response => Schedule(() => Users = response);
|
||||
api.Queue(request);
|
||||
}
|
||||
|
||||
private void recreatePanels()
|
||||
{
|
||||
if (!users.Any())
|
||||
return;
|
||||
|
||||
cancellationToken?.Cancel();
|
||||
|
||||
if (itemsPlaceholder.Any())
|
||||
loading.Show();
|
||||
|
||||
var sortedUsers = sortUsers(getUsersInCurrentGroup());
|
||||
|
||||
LoadComponentAsync(createTable(sortedUsers), addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
|
||||
private List<User> getUsersInCurrentGroup()
|
||||
{
|
||||
switch (onlineStreamControl.Current.Value?.Status)
|
||||
{
|
||||
default:
|
||||
case OnlineStatus.All:
|
||||
return users;
|
||||
|
||||
case OnlineStatus.Offline:
|
||||
return users.Where(u => !u.IsOnline).ToList();
|
||||
|
||||
case OnlineStatus.Online:
|
||||
return users.Where(u => u.IsOnline).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private void addContentToPlaceholder(Drawable content)
|
||||
{
|
||||
loading.Hide();
|
||||
|
||||
var lastContent = currentContent;
|
||||
|
||||
if (lastContent != null)
|
||||
{
|
||||
lastContent.FadeOut(100, Easing.OutQuint).Expire();
|
||||
lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y);
|
||||
}
|
||||
|
||||
itemsPlaceholder.Add(currentContent = content);
|
||||
currentContent.FadeIn(200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private FillFlowContainer createTable(List<User> users)
|
||||
{
|
||||
var style = userListToolbar.DisplayStyle.Value;
|
||||
|
||||
return new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(style == OverlayPanelDisplayStyle.Card ? 10 : 2),
|
||||
Children = users.Select(u => createUserPanel(u, style)).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private UserPanel createUserPanel(User user, OverlayPanelDisplayStyle style)
|
||||
{
|
||||
switch (style)
|
||||
{
|
||||
default:
|
||||
case OverlayPanelDisplayStyle.Card:
|
||||
return new UserGridPanel(user).With(panel =>
|
||||
{
|
||||
panel.Anchor = Anchor.TopCentre;
|
||||
panel.Origin = Anchor.TopCentre;
|
||||
panel.Width = 290;
|
||||
});
|
||||
|
||||
case OverlayPanelDisplayStyle.List:
|
||||
return new UserListPanel(user);
|
||||
}
|
||||
}
|
||||
|
||||
private List<User> sortUsers(List<User> unsorted)
|
||||
{
|
||||
switch (userListToolbar.SortCriteria.Value)
|
||||
{
|
||||
default:
|
||||
case UserSortCriteria.LastVisit:
|
||||
return unsorted.OrderByDescending(u => u.LastVisit).ToList();
|
||||
|
||||
case UserSortCriteria.Rank:
|
||||
return unsorted.OrderByDescending(u => u.CurrentModeRank.HasValue).ThenBy(u => u.CurrentModeRank ?? 0).ToList();
|
||||
|
||||
case UserSortCriteria.Username:
|
||||
return unsorted.OrderBy(u => u.Username).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
request?.Cancel();
|
||||
cancellationToken?.Cancel();
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
public class FriendOnlineStreamControl : OverlayStreamControl<FriendStream>
|
||||
{
|
||||
protected override OverlayStreamItem<FriendStream> CreateStreamItem(FriendStream value) => new FriendsOnlineStatusItem(value);
|
||||
|
||||
public void Populate(List<User> users)
|
||||
{
|
||||
Clear();
|
||||
|
||||
var userCount = users.Count;
|
||||
var onlineUsersCount = users.Count(user => user.IsOnline);
|
||||
|
||||
AddItem(new FriendStream(OnlineStatus.All, userCount));
|
||||
AddItem(new FriendStream(OnlineStatus.Online, onlineUsersCount));
|
||||
AddItem(new FriendStream(OnlineStatus.Offline, userCount - onlineUsersCount));
|
||||
|
||||
Current.Value = Items.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
18
osu.Game/Overlays/Dashboard/Friends/FriendStream.cs
Normal file
18
osu.Game/Overlays/Dashboard/Friends/FriendStream.cs
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
public class FriendStream
|
||||
{
|
||||
public OnlineStatus Status { get; }
|
||||
|
||||
public int Count { get; }
|
||||
|
||||
public FriendStream(OnlineStatus status, int count)
|
||||
{
|
||||
Status = status;
|
||||
Count = count;
|
||||
}
|
||||
}
|
||||
}
|
@ -5,11 +5,11 @@ using System;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Home.Friends
|
||||
namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
public class FriendsOnlineStatusItem : OverlayStreamItem<FriendsBundle>
|
||||
public class FriendsOnlineStatusItem : OverlayStreamItem<FriendStream>
|
||||
{
|
||||
public FriendsOnlineStatusItem(FriendsBundle value)
|
||||
public FriendsOnlineStatusItem(FriendStream value)
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
@ -22,13 +22,13 @@ namespace osu.Game.Overlays.Home.Friends
|
||||
{
|
||||
switch (Value.Status)
|
||||
{
|
||||
case FriendsOnlineStatus.All:
|
||||
case OnlineStatus.All:
|
||||
return Color4.White;
|
||||
|
||||
case FriendsOnlineStatus.Online:
|
||||
case OnlineStatus.Online:
|
||||
return colours.GreenLight;
|
||||
|
||||
case FriendsOnlineStatus.Offline:
|
||||
case OnlineStatus.Offline:
|
||||
return Color4.Black;
|
||||
|
||||
default:
|
12
osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs
Normal file
12
osu.Game/Overlays/Dashboard/Friends/OnlineStatus.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
public enum OnlineStatus
|
||||
{
|
||||
All,
|
||||
Online,
|
||||
Offline
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osuTK;
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Overlays.Home.Friends
|
||||
namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
public class UserListToolbar : CompositeDrawable
|
||||
{
|
@ -3,7 +3,7 @@
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace osu.Game.Overlays.Home.Friends
|
||||
namespace osu.Game.Overlays.Dashboard.Friends
|
||||
{
|
||||
public class UserSortTabControl : OverlaySortTabControl<UserSortCriteria>
|
||||
{
|
@ -1,25 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Overlays.Home.Friends
|
||||
{
|
||||
public class FriendsBundle
|
||||
{
|
||||
public FriendsOnlineStatus Status { get; }
|
||||
|
||||
public int Count { get; }
|
||||
|
||||
public FriendsBundle(FriendsOnlineStatus status, int count)
|
||||
{
|
||||
Status = status;
|
||||
Count = count;
|
||||
}
|
||||
}
|
||||
|
||||
public enum FriendsOnlineStatus
|
||||
{
|
||||
All,
|
||||
Online,
|
||||
Offline
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Overlays.Home.Friends
|
||||
{
|
||||
public class FriendsOnlineStatusControl : OverlayStreamControl<FriendsBundle>
|
||||
{
|
||||
protected override OverlayStreamItem<FriendsBundle> CreateStreamItem(FriendsBundle value) => new FriendsOnlineStatusItem(value);
|
||||
|
||||
public void Populate(List<User> users)
|
||||
{
|
||||
var userCount = users.Count;
|
||||
var onlineUsersCount = users.Count(user => user.IsOnline);
|
||||
|
||||
AddItem(new FriendsBundle(FriendsOnlineStatus.All, userCount));
|
||||
AddItem(new FriendsBundle(FriendsOnlineStatus.Online, onlineUsersCount));
|
||||
AddItem(new FriendsBundle(FriendsOnlineStatus.Offline, userCount - onlineUsersCount));
|
||||
|
||||
Current.Value = Items.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
@ -18,15 +18,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
{
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Storyboards",
|
||||
LabelText = "Storyboard / Video",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowStoryboard)
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Video",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowVideoBackground)
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Hit Lighting",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.HitLighting)
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework;
|
||||
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<string>(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<string>(FrameworkSetting.IgnoredInputHandlers);
|
||||
ignoredInputHandler.ValueChanged += handler =>
|
||||
{
|
||||
bool raw = !handler.NewValue.Contains("Raw");
|
||||
rawInputToggle.Value = raw;
|
||||
sensitivity.Bindable.Disabled = !raw;
|
||||
};
|
||||
|
||||
ignoredInputHandler.TriggerChange();
|
||||
}
|
||||
}
|
||||
|
||||
private class SensitivitySetting : SettingsSlider<double, SensitivitySlider>
|
||||
|
@ -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();
|
||||
|
17
osu.Game/Rulesets/Mods/ModRandom.cs
Normal file
17
osu.Game/Rulesets/Mods/ModRandom.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics.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;
|
||||
}
|
||||
}
|
@ -344,7 +344,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// Plays all the hit sounds for this <see cref="DrawableHitObject"/>.
|
||||
/// This is invoked automatically when this <see cref="DrawableHitObject"/> is hit.
|
||||
/// </summary>
|
||||
public void PlaySamples() => Samples?.Play();
|
||||
public virtual void PlaySamples() => Samples?.Play();
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
|
@ -17,6 +17,12 @@ namespace osu.Game.Rulesets.Replays.Types
|
||||
/// <param name="currentFrame">The <see cref="LegacyReplayFrame"/> to extract values from.</param>
|
||||
/// <param name="beatmap">The beatmap.</param>
|
||||
/// <param name="lastFrame">The last post-conversion <see cref="ReplayFrame"/>, used to fill in missing delta information. May be null.</param>
|
||||
void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null);
|
||||
void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null);
|
||||
|
||||
/// <summary>
|
||||
/// Populates this <see cref="ReplayFrame"/> using values from a <see cref="LegacyReplayFrame"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The beatmap.</param>
|
||||
LegacyReplayFrame ToLegacy(IBeatmap beatmap);
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +42,63 @@ namespace osu.Game.Rulesets
|
||||
/// <summary>
|
||||
/// Converts mods from legacy enum values. Do not override if you're not a legacy ruleset.
|
||||
/// </summary>
|
||||
/// <param name="mods">The legacy enum which will be converted</param>
|
||||
/// <returns>An enumerable of constructed <see cref="Mod"/>s</returns>
|
||||
public virtual IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods) => Array.Empty<Mod>();
|
||||
/// <param name="mods">The legacy enum which will be converted.</param>
|
||||
/// <returns>An enumerable of constructed <see cref="Mod"/>s.</returns>
|
||||
public virtual IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods) => Array.Empty<Mod>();
|
||||
|
||||
/// <summary>
|
||||
/// Converts mods to legacy enum values. Do not override if you're not a legacy ruleset.
|
||||
/// </summary>
|
||||
/// <param name="mods">The mods which will be converted.</param>
|
||||
/// <returns>A single bitwise enumerable value representing (to the best of our ability) the mods.</returns>
|
||||
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<ModAutoplay>().First();
|
||||
|
||||
|
@ -72,9 +72,9 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Playfield.
|
||||
/// </summary>
|
||||
@ -389,10 +407,15 @@ namespace osu.Game.Rulesets.UI
|
||||
public abstract Playfield Playfield { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Place to put drawables above hit objects but below UI.
|
||||
/// Content to be placed above hitobjects. Will be affected by frame stability.
|
||||
/// </summary>
|
||||
public abstract Container Overlays { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Components to be run potentially multiple times in line with frame-stable gameplay.
|
||||
/// </summary>
|
||||
public abstract Container FrameStableComponents { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The frame-stable clock which is being used for playfield display.
|
||||
/// </summary>
|
||||
@ -470,6 +493,12 @@ namespace osu.Game.Rulesets.UI
|
||||
/// <param name="replayScore">The replay, null for local input.</param>
|
||||
public abstract void SetReplayScore(Score replayScore);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a replay to be used to record gameplay.
|
||||
/// </summary>
|
||||
/// <param name="recordingReplay">The target to be recorded to.</param>
|
||||
public abstract void SetRecordTarget(Replay recordingReplay);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the interactive user requests resuming from a paused state.
|
||||
/// Allows potentially delaying the resume process until an interaction is performed.
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class FrameStabilityContainer : Container, IHasReplayHandler
|
||||
{
|
||||
|
@ -30,6 +30,11 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
public Func<Vector2, Vector2> GamefieldToScreenSpace => HitObjectContainer.ToScreenSpace;
|
||||
|
||||
/// <summary>
|
||||
/// A function that converts screen space coordinates to gamefield.
|
||||
/// </summary>
|
||||
public Func<Vector2, Vector2> ScreenSpaceToGamefield => HitObjectContainer.ToLocalSpace;
|
||||
|
||||
/// <summary>
|
||||
/// All the <see cref="DrawableHitObject"/>s contained in this <see cref="Playfield"/> and all <see cref="NestedPlayfields"/>.
|
||||
/// </summary>
|
||||
|
85
osu.Game/Rulesets/UI/ReplayRecorder.cs
Normal file
85
osu.Game/Rulesets/UI/ReplayRecorder.cs
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.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<T> : ReplayRecorder, IKeyBindingHandler<T>
|
||||
where T : struct
|
||||
{
|
||||
private readonly Replay target;
|
||||
|
||||
private readonly List<T> pressedActions = new List<T>();
|
||||
|
||||
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<T> actions, ReplayFrame previousFrame);
|
||||
}
|
||||
|
||||
public abstract class ReplayRecorder : Component
|
||||
{
|
||||
public Func<Vector2, Vector2> ScreenSpaceToGamefield;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.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<T> : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler
|
||||
public abstract class RulesetInputManager<T> : 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<T> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
=> new RulesetKeyBindingContainer(ruleset, variant, unique);
|
||||
|
||||
public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer<T>
|
||||
@ -168,6 +184,11 @@ namespace osu.Game.Rulesets.UI
|
||||
ReplayInputHandler ReplayInputHandler { get; set; }
|
||||
}
|
||||
|
||||
public interface IHasRecordingHandler
|
||||
{
|
||||
public ReplayRecorder Recorder { set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Supports attaching a <see cref="KeyCounterDisplay"/>.
|
||||
/// Keys will be populated automatically and a receptor will be injected inside.
|
||||
|
@ -7,15 +7,15 @@ using osu.Game.Rulesets;
|
||||
namespace osu.Game.Scoring.Legacy
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="LegacyScoreParser"/> which retrieves the applicable <see cref="Beatmap"/> and <see cref="Ruleset"/>
|
||||
/// A <see cref="LegacyScoreDecoder"/> which retrieves the applicable <see cref="Beatmap"/> and <see cref="Ruleset"/>
|
||||
/// for the score from the database.
|
||||
/// </summary>
|
||||
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;
|
@ -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;
|
114
osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs
Normal file
114
osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.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<IConvertibleReplayFrame>().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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// The duration of the break overlay fading.
|
||||
/// </summary>
|
||||
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the gameplay is currently in a break.
|
||||
/// </summary>
|
||||
public IBindable<bool> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
82
osu.Game/Screens/Play/BreakTracker.cs
Normal file
82
osu.Game/Screens/Play/BreakTracker.cs
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.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;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the gameplay is currently in a break.
|
||||
/// </summary>
|
||||
public IBindable<bool> IsBreakTime => isBreakTime;
|
||||
|
||||
protected int CurrentBreakIndex;
|
||||
|
||||
private readonly BindableBool isBreakTime = new BindableBool();
|
||||
|
||||
private IReadOnlyList<BreakPeriod> breaks;
|
||||
|
||||
public IReadOnlyList<BreakPeriod> 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -44,7 +44,6 @@ namespace osu.Game.Screens.Play
|
||||
return;
|
||||
|
||||
drawableStoryboard = storyboard.CreateDrawable();
|
||||
drawableStoryboard.Masking = true;
|
||||
|
||||
if (async)
|
||||
LoadComponentAsync(drawableStoryboard, Add);
|
||||
|
@ -1,88 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<IReadOnlyList<Mod>>))]
|
||||
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Run any recording / playback setup for replays.
|
||||
/// </summary>
|
||||
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<IApplicableToHealthProcessor>())
|
||||
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<bool> 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));
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
private readonly PlayerSliderBar<double> dimSliderBar;
|
||||
private readonly PlayerSliderBar<double> 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<double>(OsuSetting.DimLevel);
|
||||
blurSliderBar.Bindable = config.GetBindable<double>(OsuSetting.BlurLevel);
|
||||
showStoryboardToggle.Current = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||
showVideoToggle.Current = config.GetBindable<bool>(OsuSetting.ShowVideoBackground);
|
||||
beatmapSkinsToggle.Current = config.GetBindable<bool>(OsuSetting.BeatmapSkins);
|
||||
beatmapHitsoundsToggle.Current = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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()));
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
64
osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs
Normal file
64
osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using 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<WorkingBeatmap> 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);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user