mirror of
https://github.com/osukey/osukey.git
synced 2025-05-18 03:57:32 +09:00
Merge remote-tracking branch 'upstream/master' into resume-cursor-2
This commit is contained in:
commit
6949c233bf
@ -18,6 +18,7 @@ using osu.Game.Beatmaps.Legacy;
|
|||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Difficulty;
|
using osu.Game.Rulesets.Catch.Difficulty;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch
|
namespace osu.Game.Rulesets.Catch
|
||||||
{
|
{
|
||||||
@ -119,6 +120,8 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
|
|
||||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
|
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
|
||||||
|
|
||||||
|
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new CatchPerformanceCalculator(this, beatmap, score);
|
||||||
|
|
||||||
public override int? LegacyID => 2;
|
public override int? LegacyID => 2;
|
||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
||||||
|
104
osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs
Normal file
104
osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// 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.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Scoring.Legacy;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||||
|
{
|
||||||
|
public class CatchPerformanceCalculator : PerformanceCalculator
|
||||||
|
{
|
||||||
|
protected new CatchDifficultyAttributes Attributes => (CatchDifficultyAttributes)base.Attributes;
|
||||||
|
|
||||||
|
private Mod[] mods;
|
||||||
|
|
||||||
|
private int fruitsHit;
|
||||||
|
private int ticksHit;
|
||||||
|
private int tinyTicksHit;
|
||||||
|
private int tinyTicksMissed;
|
||||||
|
private int misses;
|
||||||
|
|
||||||
|
public CatchPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, ScoreInfo score)
|
||||||
|
: base(ruleset, beatmap, score)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||||
|
{
|
||||||
|
mods = Score.Mods;
|
||||||
|
|
||||||
|
var legacyScore = Score as LegacyScoreInfo;
|
||||||
|
|
||||||
|
fruitsHit = legacyScore?.Count300 ?? Score.Statistics[HitResult.Perfect];
|
||||||
|
ticksHit = legacyScore?.Count100 ?? 0;
|
||||||
|
tinyTicksHit = legacyScore?.Count50 ?? 0;
|
||||||
|
tinyTicksMissed = legacyScore?.CountKatu ?? 0;
|
||||||
|
misses = Score.Statistics[HitResult.Miss];
|
||||||
|
|
||||||
|
// Don't count scores made with supposedly unranked mods
|
||||||
|
if (mods.Any(m => !m.Ranked))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// We are heavily relying on aim in catch the beat
|
||||||
|
double value = Math.Pow(5.0f * Math.Max(1.0f, Attributes.StarRating / 0.0049f) - 4.0f, 2.0f) / 100000.0f;
|
||||||
|
|
||||||
|
// Longer maps are worth more. "Longer" means how many hits there are which can contribute to combo
|
||||||
|
int numTotalHits = totalComboHits();
|
||||||
|
|
||||||
|
// Longer maps are worth more
|
||||||
|
float lengthBonus =
|
||||||
|
0.95f + 0.4f * Math.Min(1.0f, numTotalHits / 3000.0f) +
|
||||||
|
(numTotalHits > 3000 ? (float)Math.Log10(numTotalHits / 3000.0f) * 0.5f : 0.0f);
|
||||||
|
|
||||||
|
// Longer maps are worth more
|
||||||
|
value *= lengthBonus;
|
||||||
|
|
||||||
|
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
||||||
|
value *= Math.Pow(0.97f, misses);
|
||||||
|
|
||||||
|
// Combo scaling
|
||||||
|
float beatmapMaxCombo = Attributes.MaxCombo;
|
||||||
|
if (beatmapMaxCombo > 0)
|
||||||
|
value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
|
||||||
|
|
||||||
|
float approachRate = (float)Attributes.ApproachRate;
|
||||||
|
float approachRateFactor = 1.0f;
|
||||||
|
if (approachRate > 9.0f)
|
||||||
|
approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9
|
||||||
|
else if (approachRate < 8.0f)
|
||||||
|
approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8
|
||||||
|
|
||||||
|
value *= approachRateFactor;
|
||||||
|
|
||||||
|
if (mods.Any(m => m is ModHidden))
|
||||||
|
// Hiddens gives nothing on max approach rate, and more the lower it is
|
||||||
|
value *= 1.05f + 0.075f * (10.0f - Math.Min(10.0f, approachRate)); // 7.5% for each AR below 10
|
||||||
|
|
||||||
|
if (mods.Any(m => m is ModFlashlight))
|
||||||
|
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
|
||||||
|
value *= 1.35f * lengthBonus;
|
||||||
|
|
||||||
|
// Scale the aim value with accuracy _slightly_
|
||||||
|
value *= Math.Pow(accuracy(), 5.5f);
|
||||||
|
|
||||||
|
// Custom multipliers for NoFail. SpunOut is not applicable.
|
||||||
|
if (mods.Any(m => m is ModNoFail))
|
||||||
|
value *= 0.90f;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float accuracy() => totalHits() == 0 ? 0 : MathHelper.Clamp((float)totalSuccessfulHits() / totalHits(), 0f, 1f);
|
||||||
|
private int totalHits() => tinyTicksHit + ticksHit + fruitsHit + misses + tinyTicksMissed;
|
||||||
|
private int totalSuccessfulHits() => tinyTicksHit + ticksHit + fruitsHit;
|
||||||
|
private int totalComboHits() => misses + ticksHit + fruitsHit;
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
{
|
{
|
||||||
private readonly Container bananaContainer;
|
private readonly Container bananaContainer;
|
||||||
|
|
||||||
public DrawableBananaShower(BananaShower s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
|
public DrawableBananaShower(BananaShower s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation = null)
|
||||||
: base(s)
|
: base(s)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
AddInternal(bananaContainer = new Container { RelativeSizeAxes = Axes.Both });
|
AddInternal(bananaContainer = new Container { RelativeSizeAxes = Axes.Both });
|
||||||
|
|
||||||
foreach (var b in s.NestedHitObjects.Cast<Banana>())
|
foreach (var b in s.NestedHitObjects.Cast<Banana>())
|
||||||
AddNested(getVisualRepresentation?.Invoke(b));
|
AddNested(createDrawableRepresentation?.Invoke(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddNested(DrawableHitObject h)
|
protected override void AddNested(DrawableHitObject h)
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
{
|
{
|
||||||
private readonly Container dropletContainer;
|
private readonly Container dropletContainer;
|
||||||
|
|
||||||
public DrawableJuiceStream(JuiceStream s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
|
public DrawableJuiceStream(JuiceStream s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation = null)
|
||||||
: base(s)
|
: base(s)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
AddInternal(dropletContainer = new Container { RelativeSizeAxes = Axes.Both, });
|
AddInternal(dropletContainer = new Container { RelativeSizeAxes = Axes.Both, });
|
||||||
|
|
||||||
foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
|
foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
|
||||||
AddNested(getVisualRepresentation?.Invoke(o));
|
AddNested(createDrawableRepresentation?.Invoke(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddNested(DrawableHitObject h)
|
protected override void AddNested(DrawableHitObject h)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Input.StateChanges;
|
using osu.Framework.Input.StateChanges;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
@ -22,10 +23,14 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!HasFrames)
|
var frame = CurrentFrame;
|
||||||
|
|
||||||
|
if (frame == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
|
Debug.Assert(CurrentTime != null);
|
||||||
|
|
||||||
|
return NextFrame != null ? Interpolation.ValueAt(CurrentTime.Value, frame.Position, NextFrame.Position, frame.Time, NextFrame.Time) : frame.Position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
internal readonly CatcherArea CatcherArea;
|
internal readonly CatcherArea CatcherArea;
|
||||||
|
|
||||||
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
|
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation)
|
||||||
{
|
{
|
||||||
Container explodingFruitContainer;
|
Container explodingFruitContainer;
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
},
|
},
|
||||||
CatcherArea = new CatcherArea(difficulty)
|
CatcherArea = new CatcherArea(difficulty)
|
||||||
{
|
{
|
||||||
GetVisualRepresentation = getVisualRepresentation,
|
CreateDrawableRepresentation = createDrawableRepresentation,
|
||||||
ExplodingFruitTarget = explodingFruitContainer,
|
ExplodingFruitTarget = explodingFruitContainer,
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
protected internal readonly Catcher MovableCatcher;
|
protected internal readonly Catcher MovableCatcher;
|
||||||
|
|
||||||
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> GetVisualRepresentation;
|
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> CreateDrawableRepresentation;
|
||||||
|
|
||||||
public Container ExplodingFruitTarget
|
public Container ExplodingFruitTarget
|
||||||
{
|
{
|
||||||
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
if (result.IsHit && fruit.CanBePlated)
|
if (result.IsHit && fruit.CanBePlated)
|
||||||
{
|
{
|
||||||
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
|
var caughtFruit = (DrawableCatchHitObject)CreateDrawableRepresentation?.Invoke(fruit.HitObject);
|
||||||
|
|
||||||
if (caughtFruit == null) return;
|
if (caughtFruit == null) return;
|
||||||
|
|
||||||
|
@ -34,13 +34,13 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation);
|
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation);
|
||||||
|
|
||||||
protected override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchPlayfieldAdjustmentContainer();
|
protected override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchPlayfieldAdjustmentContainer();
|
||||||
|
|
||||||
protected override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
|
protected override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
|
||||||
|
|
||||||
public override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
|
public override DrawableHitObject<CatchHitObject> CreateDrawableRepresentation(CatchHitObject h)
|
||||||
{
|
{
|
||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
@ -49,9 +49,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
case Fruit fruit:
|
case Fruit fruit:
|
||||||
return new DrawableFruit(fruit);
|
return new DrawableFruit(fruit);
|
||||||
case JuiceStream stream:
|
case JuiceStream stream:
|
||||||
return new DrawableJuiceStream(stream, GetVisualRepresentation);
|
return new DrawableJuiceStream(stream, CreateDrawableRepresentation);
|
||||||
case BananaShower shower:
|
case BananaShower shower:
|
||||||
return new DrawableBananaShower(shower, GetVisualRepresentation);
|
return new DrawableBananaShower(shower, CreateDrawableRepresentation);
|
||||||
case TinyDroplet tiny:
|
case TinyDroplet tiny:
|
||||||
return new DrawableTinyDroplet(tiny);
|
return new DrawableTinyDroplet(tiny);
|
||||||
case Droplet droplet:
|
case Droplet droplet:
|
||||||
|
@ -18,6 +18,6 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
|
|
||||||
protected override bool IsImportant(ManiaReplayFrame frame) => frame.Actions.Any();
|
protected override bool IsImportant(ManiaReplayFrame frame) => frame.Actions.Any();
|
||||||
|
|
||||||
public override List<IInput> GetPendingInputs() => new List<IInput> { new ReplayState<ManiaAction> { PressedActions = CurrentFrame.Actions } };
|
public override List<IInput> GetPendingInputs() => new List<IInput> { new ReplayState<ManiaAction> { PressedActions = CurrentFrame?.Actions ?? new List<ManiaAction>() } };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
|
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
|
||||||
|
|
||||||
public override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h)
|
public override DrawableHitObject<ManiaHitObject> CreateDrawableRepresentation(ManiaHitObject h)
|
||||||
{
|
{
|
||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
|
@ -297,11 +297,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private void performTest(List<ReplayFrame> frames)
|
private void performTest(List<ReplayFrame> frames)
|
||||||
{
|
{
|
||||||
// Empty frame to be added as a workaround for first frame behavior.
|
|
||||||
// If an input exists on the first frame, the input would apply to the entire intro lead-in
|
|
||||||
// Likely requires some discussion regarding how first frame inputs should be handled.
|
|
||||||
frames.Insert(0, new OsuReplayFrame());
|
|
||||||
|
|
||||||
AddStep("load player", () =>
|
AddStep("load player", () =>
|
||||||
{
|
{
|
||||||
Beatmap.Value = new TestWorkingBeatmap(new Beatmap<OsuHitObject>
|
Beatmap.Value = new TestWorkingBeatmap(new Beatmap<OsuHitObject>
|
||||||
@ -330,12 +325,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
},
|
},
|
||||||
}, Clock);
|
}, Clock);
|
||||||
|
|
||||||
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } })
|
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
|
||||||
{
|
|
||||||
AllowPause = false,
|
|
||||||
AllowLeadIn = false,
|
|
||||||
AllowResults = false
|
|
||||||
};
|
|
||||||
|
|
||||||
p.OnLoadComplete += _ =>
|
p.OnLoadComplete += _ =>
|
||||||
{
|
{
|
||||||
@ -364,7 +354,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
||||||
|
|
||||||
public ScoreAccessibleReplayPlayer(Score score)
|
public ScoreAccessibleReplayPlayer(Score score)
|
||||||
: base(score)
|
: base(score, false, false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Input.StateChanges;
|
using osu.Framework.Input.StateChanges;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
@ -18,16 +19,20 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool IsImportant(OsuReplayFrame frame) => frame.Actions.Any();
|
protected override bool IsImportant(OsuReplayFrame frame) => frame?.Actions.Any() ?? false;
|
||||||
|
|
||||||
protected Vector2? Position
|
protected Vector2? Position
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!HasFrames)
|
var frame = CurrentFrame;
|
||||||
|
|
||||||
|
if (frame == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
|
Debug.Assert(CurrentTime != null);
|
||||||
|
|
||||||
|
return NextFrame != null ? Interpolation.ValueAt(CurrentTime.Value, frame.Position, NextFrame.Position, frame.Time, NextFrame.Time) : frame.Position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
},
|
},
|
||||||
new ReplayState<OsuAction>
|
new ReplayState<OsuAction>
|
||||||
{
|
{
|
||||||
PressedActions = CurrentFrame.Actions
|
PressedActions = CurrentFrame?.Actions ?? new List<OsuAction>()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay();
|
protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay();
|
||||||
|
|
||||||
public override DrawableHitObject<OsuHitObject> GetVisualRepresentation(OsuHitObject h)
|
public override DrawableHitObject<OsuHitObject> CreateDrawableRepresentation(OsuHitObject h)
|
||||||
{
|
{
|
||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,6 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
|||||||
|
|
||||||
protected override bool IsImportant(TaikoReplayFrame frame) => frame.Actions.Any();
|
protected override bool IsImportant(TaikoReplayFrame frame) => frame.Actions.Any();
|
||||||
|
|
||||||
public override List<IInput> GetPendingInputs() => new List<IInput> { new ReplayState<TaikoAction> { PressedActions = CurrentFrame.Actions } };
|
public override List<IInput> GetPendingInputs() => new List<IInput> { new ReplayState<TaikoAction> { PressedActions = CurrentFrame?.Actions ?? new List<TaikoAction>() } };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo);
|
protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo);
|
||||||
|
|
||||||
public override DrawableHitObject<TaikoHitObject> GetVisualRepresentation(TaikoHitObject h)
|
public override DrawableHitObject<TaikoHitObject> CreateDrawableRepresentation(TaikoHitObject h)
|
||||||
{
|
{
|
||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
|
@ -29,28 +29,28 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
|
|
||||||
StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
|
StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
|
||||||
Assert.IsNotNull(background);
|
Assert.IsNotNull(background);
|
||||||
Assert.AreEqual(16, background.Elements.Count());
|
Assert.AreEqual(16, background.Elements.Count);
|
||||||
Assert.IsTrue(background.EnabledWhenFailing);
|
Assert.IsTrue(background.EnabledWhenFailing);
|
||||||
Assert.IsTrue(background.EnabledWhenPassing);
|
Assert.IsTrue(background.EnabledWhenPassing);
|
||||||
Assert.AreEqual("Background", background.Name);
|
Assert.AreEqual("Background", background.Name);
|
||||||
|
|
||||||
StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2);
|
StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2);
|
||||||
Assert.IsNotNull(fail);
|
Assert.IsNotNull(fail);
|
||||||
Assert.AreEqual(0, fail.Elements.Count());
|
Assert.AreEqual(0, fail.Elements.Count);
|
||||||
Assert.IsTrue(fail.EnabledWhenFailing);
|
Assert.IsTrue(fail.EnabledWhenFailing);
|
||||||
Assert.IsFalse(fail.EnabledWhenPassing);
|
Assert.IsFalse(fail.EnabledWhenPassing);
|
||||||
Assert.AreEqual("Fail", fail.Name);
|
Assert.AreEqual("Fail", fail.Name);
|
||||||
|
|
||||||
StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1);
|
StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1);
|
||||||
Assert.IsNotNull(pass);
|
Assert.IsNotNull(pass);
|
||||||
Assert.AreEqual(0, pass.Elements.Count());
|
Assert.AreEqual(0, pass.Elements.Count);
|
||||||
Assert.IsFalse(pass.EnabledWhenFailing);
|
Assert.IsFalse(pass.EnabledWhenFailing);
|
||||||
Assert.IsTrue(pass.EnabledWhenPassing);
|
Assert.IsTrue(pass.EnabledWhenPassing);
|
||||||
Assert.AreEqual("Pass", pass.Name);
|
Assert.AreEqual("Pass", pass.Name);
|
||||||
|
|
||||||
StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0);
|
StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0);
|
||||||
Assert.IsNotNull(foreground);
|
Assert.IsNotNull(foreground);
|
||||||
Assert.AreEqual(151, foreground.Elements.Count());
|
Assert.AreEqual(151, foreground.Elements.Count);
|
||||||
Assert.IsTrue(foreground.EnabledWhenFailing);
|
Assert.IsTrue(foreground.EnabledWhenFailing);
|
||||||
Assert.IsTrue(foreground.EnabledWhenPassing);
|
Assert.IsTrue(foreground.EnabledWhenPassing);
|
||||||
Assert.AreEqual("Foreground", foreground.Name);
|
Assert.AreEqual("Foreground", foreground.Name);
|
||||||
@ -62,7 +62,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual(15, spriteCount);
|
Assert.AreEqual(15, spriteCount);
|
||||||
Assert.AreEqual(1, animationCount);
|
Assert.AreEqual(1, animationCount);
|
||||||
Assert.AreEqual(0, sampleCount);
|
Assert.AreEqual(0, sampleCount);
|
||||||
Assert.AreEqual(background.Elements.Count(), spriteCount + animationCount + sampleCount);
|
Assert.AreEqual(background.Elements.Count, spriteCount + animationCount + sampleCount);
|
||||||
|
|
||||||
var sprite = background.Elements.ElementAt(0) as StoryboardSprite;
|
var sprite = background.Elements.ElementAt(0) as StoryboardSprite;
|
||||||
Assert.NotNull(sprite);
|
Assert.NotNull(sprite);
|
||||||
@ -70,9 +70,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
|
Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
|
||||||
Assert.IsTrue(sprite.IsDrawable);
|
Assert.IsTrue(sprite.IsDrawable);
|
||||||
Assert.AreEqual(Anchor.Centre, sprite.Origin);
|
Assert.AreEqual(Anchor.Centre, sprite.Origin);
|
||||||
Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path);
|
Assert.AreEqual("SB/black.jpg", sprite.Path);
|
||||||
|
|
||||||
var animation = background.Elements.ElementAt(12) as StoryboardAnimation;
|
var animation = background.Elements.OfType<StoryboardAnimation>().First();
|
||||||
Assert.NotNull(animation);
|
Assert.NotNull(animation);
|
||||||
Assert.AreEqual(141175, animation.EndTime);
|
Assert.AreEqual(141175, animation.EndTime);
|
||||||
Assert.AreEqual(10, animation.FrameCount);
|
Assert.AreEqual(10, animation.FrameCount);
|
||||||
|
284
osu.Game.Tests/NonVisual/FramedReplayinputHandlerTest.cs
Normal file
284
osu.Game.Tests/NonVisual/FramedReplayinputHandlerTest.cs
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Game.Replays;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.NonVisual
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class FramedReplayinputHandlerTest
|
||||||
|
{
|
||||||
|
private Replay replay;
|
||||||
|
private TestInputHandler handler;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
handler = new TestInputHandler(replay = new Replay
|
||||||
|
{
|
||||||
|
Frames = new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new TestReplayFrame(0),
|
||||||
|
new TestReplayFrame(1000),
|
||||||
|
new TestReplayFrame(2000),
|
||||||
|
new TestReplayFrame(3000, true),
|
||||||
|
new TestReplayFrame(4000, true),
|
||||||
|
new TestReplayFrame(5000, true),
|
||||||
|
new TestReplayFrame(7000, true),
|
||||||
|
new TestReplayFrame(8000),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNormalPlayback()
|
||||||
|
{
|
||||||
|
Assert.IsNull(handler.CurrentFrame);
|
||||||
|
|
||||||
|
confirmCurrentFrame(null);
|
||||||
|
confirmNextFrame(0);
|
||||||
|
|
||||||
|
setTime(0, 0);
|
||||||
|
confirmCurrentFrame(0);
|
||||||
|
confirmNextFrame(1);
|
||||||
|
|
||||||
|
//if we hit the first frame perfectly, time should progress to it.
|
||||||
|
setTime(1000, 1000);
|
||||||
|
confirmCurrentFrame(1);
|
||||||
|
confirmNextFrame(2);
|
||||||
|
|
||||||
|
//in between non-important frames should progress based on input.
|
||||||
|
setTime(1200, 1200);
|
||||||
|
confirmCurrentFrame(1);
|
||||||
|
|
||||||
|
setTime(1400, 1400);
|
||||||
|
confirmCurrentFrame(1);
|
||||||
|
|
||||||
|
// progressing beyond the next frame should force time to that frame once.
|
||||||
|
setTime(2200, 2000);
|
||||||
|
confirmCurrentFrame(2);
|
||||||
|
|
||||||
|
// second attempt should progress to input time
|
||||||
|
setTime(2200, 2200);
|
||||||
|
confirmCurrentFrame(2);
|
||||||
|
|
||||||
|
// entering important section
|
||||||
|
setTime(3000, 3000);
|
||||||
|
confirmCurrentFrame(3);
|
||||||
|
|
||||||
|
// cannot progress within
|
||||||
|
setTime(3500, null);
|
||||||
|
confirmCurrentFrame(3);
|
||||||
|
|
||||||
|
setTime(4000, 4000);
|
||||||
|
confirmCurrentFrame(4);
|
||||||
|
|
||||||
|
// still cannot progress
|
||||||
|
setTime(4500, null);
|
||||||
|
confirmCurrentFrame(4);
|
||||||
|
|
||||||
|
setTime(5200, 5000);
|
||||||
|
confirmCurrentFrame(5);
|
||||||
|
|
||||||
|
// important section AllowedImportantTimeSpan allowance
|
||||||
|
setTime(5200, 5200);
|
||||||
|
confirmCurrentFrame(5);
|
||||||
|
|
||||||
|
setTime(7200, 7000);
|
||||||
|
confirmCurrentFrame(6);
|
||||||
|
|
||||||
|
setTime(7200, null);
|
||||||
|
confirmCurrentFrame(6);
|
||||||
|
|
||||||
|
// exited important section
|
||||||
|
setTime(8200, 8000);
|
||||||
|
confirmCurrentFrame(7);
|
||||||
|
confirmNextFrame(null);
|
||||||
|
|
||||||
|
setTime(8200, 8200);
|
||||||
|
confirmCurrentFrame(7);
|
||||||
|
confirmNextFrame(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestIntroTime()
|
||||||
|
{
|
||||||
|
setTime(-1000, -1000);
|
||||||
|
confirmCurrentFrame(null);
|
||||||
|
confirmNextFrame(0);
|
||||||
|
|
||||||
|
setTime(-500, -500);
|
||||||
|
confirmCurrentFrame(null);
|
||||||
|
confirmNextFrame(0);
|
||||||
|
|
||||||
|
setTime(0, 0);
|
||||||
|
confirmCurrentFrame(0);
|
||||||
|
confirmNextFrame(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasicRewind()
|
||||||
|
{
|
||||||
|
setTime(2800, 0);
|
||||||
|
setTime(2800, 1000);
|
||||||
|
setTime(2800, 2000);
|
||||||
|
setTime(2800, 2800);
|
||||||
|
confirmCurrentFrame(2);
|
||||||
|
confirmNextFrame(3);
|
||||||
|
|
||||||
|
// pivot without crossing a frame boundary
|
||||||
|
setTime(2700, 2700);
|
||||||
|
confirmCurrentFrame(2);
|
||||||
|
confirmNextFrame(1);
|
||||||
|
|
||||||
|
// cross current frame boundary; should not yet update frame
|
||||||
|
setTime(1980, 1980);
|
||||||
|
confirmCurrentFrame(2);
|
||||||
|
confirmNextFrame(1);
|
||||||
|
|
||||||
|
setTime(1200, 1200);
|
||||||
|
confirmCurrentFrame(2);
|
||||||
|
confirmNextFrame(1);
|
||||||
|
|
||||||
|
//ensure each frame plays out until start
|
||||||
|
setTime(-500, 1000);
|
||||||
|
confirmCurrentFrame(1);
|
||||||
|
confirmNextFrame(0);
|
||||||
|
|
||||||
|
setTime(-500, 0);
|
||||||
|
confirmCurrentFrame(0);
|
||||||
|
confirmNextFrame(null);
|
||||||
|
|
||||||
|
setTime(-500, -500);
|
||||||
|
confirmCurrentFrame(0);
|
||||||
|
confirmNextFrame(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRewindInsideImportantSection()
|
||||||
|
{
|
||||||
|
// fast forward to important section
|
||||||
|
while (handler.SetFrameFromTime(3000) != null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
setTime(4000, 4000);
|
||||||
|
confirmCurrentFrame(4);
|
||||||
|
confirmNextFrame(5);
|
||||||
|
|
||||||
|
setTime(3500, null);
|
||||||
|
confirmCurrentFrame(4);
|
||||||
|
confirmNextFrame(3);
|
||||||
|
|
||||||
|
setTime(3000, 3000);
|
||||||
|
confirmCurrentFrame(3);
|
||||||
|
confirmNextFrame(2);
|
||||||
|
|
||||||
|
setTime(3500, null);
|
||||||
|
confirmCurrentFrame(3);
|
||||||
|
confirmNextFrame(4);
|
||||||
|
|
||||||
|
setTime(4000, 4000);
|
||||||
|
confirmCurrentFrame(4);
|
||||||
|
confirmNextFrame(5);
|
||||||
|
|
||||||
|
setTime(4500, null);
|
||||||
|
confirmCurrentFrame(4);
|
||||||
|
confirmNextFrame(5);
|
||||||
|
|
||||||
|
setTime(4000, null);
|
||||||
|
confirmCurrentFrame(4);
|
||||||
|
confirmNextFrame(5);
|
||||||
|
|
||||||
|
setTime(3500, null);
|
||||||
|
confirmCurrentFrame(4);
|
||||||
|
confirmNextFrame(3);
|
||||||
|
|
||||||
|
setTime(3000, 3000);
|
||||||
|
confirmCurrentFrame(3);
|
||||||
|
confirmNextFrame(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRewindOutOfImportantSection()
|
||||||
|
{
|
||||||
|
// fast forward to important section
|
||||||
|
while (handler.SetFrameFromTime(3500) != null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmCurrentFrame(3);
|
||||||
|
confirmNextFrame(4);
|
||||||
|
|
||||||
|
setTime(3200, null);
|
||||||
|
// next frame doesn't change even though direction reversed, because of important section.
|
||||||
|
confirmCurrentFrame(3);
|
||||||
|
confirmNextFrame(4);
|
||||||
|
|
||||||
|
setTime(3000, null);
|
||||||
|
confirmCurrentFrame(3);
|
||||||
|
confirmNextFrame(4);
|
||||||
|
|
||||||
|
setTime(2800, 2800);
|
||||||
|
confirmCurrentFrame(3);
|
||||||
|
confirmNextFrame(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTime(double set, double? expect)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(expect, handler.SetFrameFromTime(set));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void confirmCurrentFrame(int? frame)
|
||||||
|
{
|
||||||
|
if (frame.HasValue)
|
||||||
|
{
|
||||||
|
Assert.IsNotNull(handler.CurrentFrame);
|
||||||
|
Assert.AreEqual(replay.Frames[frame.Value].Time, handler.CurrentFrame.Time);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.IsNull(handler.CurrentFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void confirmNextFrame(int? frame)
|
||||||
|
{
|
||||||
|
if (frame.HasValue)
|
||||||
|
{
|
||||||
|
Assert.IsNotNull(handler.NextFrame);
|
||||||
|
Assert.AreEqual(replay.Frames[frame.Value].Time, handler.NextFrame.Time);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.IsNull(handler.NextFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestReplayFrame : ReplayFrame
|
||||||
|
{
|
||||||
|
public readonly bool IsImportant;
|
||||||
|
|
||||||
|
public TestReplayFrame(double time, bool isImportant = false)
|
||||||
|
: base(time)
|
||||||
|
{
|
||||||
|
IsImportant = isImportant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestInputHandler : FramedReplayInputHandler<TestReplayFrame>
|
||||||
|
{
|
||||||
|
public TestInputHandler(Replay replay)
|
||||||
|
: base(replay)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double AllowedImportantTimeSpan => 1000;
|
||||||
|
|
||||||
|
protected override bool IsImportant(TestReplayFrame frame) => frame?.IsImportant ?? false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -255,7 +255,8 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
{
|
{
|
||||||
setupUserSettings();
|
setupUserSettings();
|
||||||
|
|
||||||
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer { AllowPause = allowPause, })));
|
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer(allowPause))));
|
||||||
|
|
||||||
AddUntilStep("Wait for Player Loader to load", () => playerLoader.IsLoaded);
|
AddUntilStep("Wait for Player Loader to load", () => playerLoader.IsLoaded);
|
||||||
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
|
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
|
||||||
AddUntilStep("Wait for player to load", () => player.IsLoaded);
|
AddUntilStep("Wait for player to load", () => player.IsLoaded);
|
||||||
@ -350,6 +351,11 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
public readonly Bindable<bool> ReplacesBackground = new Bindable<bool>();
|
public readonly Bindable<bool> ReplacesBackground = new Bindable<bool>();
|
||||||
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
|
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
|
||||||
|
|
||||||
|
public TestPlayer(bool allowPause = true)
|
||||||
|
: base(allowPause)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsStoryboardVisible() => ((TestUserDimContainer)CurrentStoryboardContainer).CurrentAlpha == 1;
|
public bool IsStoryboardVisible() => ((TestUserDimContainer)CurrentStoryboardContainer).CurrentAlpha == 1;
|
||||||
|
|
||||||
public bool IsStoryboardInvisible() => ((TestUserDimContainer)CurrentStoryboardContainer).CurrentAlpha <= 1;
|
public bool IsStoryboardInvisible() => ((TestUserDimContainer)CurrentStoryboardContainer).CurrentAlpha <= 1;
|
||||||
|
@ -15,12 +15,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
protected override Player CreatePlayer(Ruleset ruleset)
|
protected override Player CreatePlayer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
|
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
|
||||||
return new ScoreAccessiblePlayer
|
return new ScoreAccessiblePlayer();
|
||||||
{
|
|
||||||
AllowPause = false,
|
|
||||||
AllowLeadIn = false,
|
|
||||||
AllowResults = false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddCheckSteps()
|
protected override void AddCheckSteps()
|
||||||
@ -33,6 +28,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
||||||
public new HUDOverlay HUDOverlay => base.HUDOverlay;
|
public new HUDOverlay HUDOverlay => base.HUDOverlay;
|
||||||
|
|
||||||
|
public ScoreAccessiblePlayer()
|
||||||
|
: base(false, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
Beatmap.Value = new DummyWorkingBeatmap(game);
|
Beatmap.Value = new DummyWorkingBeatmap(game);
|
||||||
|
|
||||||
AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player
|
AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player(false, false))));
|
||||||
{
|
|
||||||
AllowPause = false,
|
|
||||||
AllowLeadIn = false,
|
|
||||||
AllowResults = false,
|
|
||||||
})));
|
|
||||||
|
|
||||||
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
|
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
|
||||||
|
|
||||||
@ -47,12 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
SlowLoadPlayer slow = null;
|
SlowLoadPlayer slow = null;
|
||||||
|
|
||||||
stack.Push(loader = new PlayerLoader(() => slow = new SlowLoadPlayer
|
stack.Push(loader = new PlayerLoader(() => slow = new SlowLoadPlayer(false, false)));
|
||||||
{
|
|
||||||
AllowPause = false,
|
|
||||||
AllowLeadIn = false,
|
|
||||||
AllowResults = false,
|
|
||||||
}));
|
|
||||||
|
|
||||||
Scheduler.AddDelayed(() => slow.Ready = true, 5000);
|
Scheduler.AddDelayed(() => slow.Ready = true, 5000);
|
||||||
});
|
});
|
||||||
@ -64,6 +54,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
public bool Ready;
|
public bool Ready;
|
||||||
|
|
||||||
|
public SlowLoadPlayer(bool allowPause = true, bool showResults = true)
|
||||||
|
: base(allowPause, showResults)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
|
||||||
using osu.Game.Overlays.BeatmapSet.Scores;
|
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
@ -44,9 +43,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
IEnumerable<APIScoreInfo> scores = new[]
|
IEnumerable<ScoreInfo> scores = new[]
|
||||||
{
|
{
|
||||||
new APIScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
@ -69,7 +68,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
TotalScore = 1234567890,
|
TotalScore = 1234567890,
|
||||||
Accuracy = 1,
|
Accuracy = 1,
|
||||||
},
|
},
|
||||||
new APIScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
@ -91,7 +90,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
TotalScore = 1234789,
|
TotalScore = 1234789,
|
||||||
Accuracy = 0.9997,
|
Accuracy = 0.9997,
|
||||||
},
|
},
|
||||||
new APIScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
@ -112,7 +111,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
TotalScore = 12345678,
|
TotalScore = 12345678,
|
||||||
Accuracy = 0.9854,
|
Accuracy = 0.9854,
|
||||||
},
|
},
|
||||||
new APIScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
@ -132,7 +131,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
TotalScore = 1234567,
|
TotalScore = 1234567,
|
||||||
Accuracy = 0.8765,
|
Accuracy = 0.8765,
|
||||||
},
|
},
|
||||||
new APIScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
@ -157,9 +156,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
|
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<APIScoreInfo> anotherScores = new[]
|
IEnumerable<ScoreInfo> anotherScores = new[]
|
||||||
{
|
{
|
||||||
new APIScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
@ -181,7 +180,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
TotalScore = 1234789,
|
TotalScore = 1234789,
|
||||||
Accuracy = 0.9997,
|
Accuracy = 0.9997,
|
||||||
},
|
},
|
||||||
new APIScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
@ -204,7 +203,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
TotalScore = 1234567890,
|
TotalScore = 1234567890,
|
||||||
Accuracy = 1,
|
Accuracy = 1,
|
||||||
},
|
},
|
||||||
new APIScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
@ -220,7 +219,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
TotalScore = 123456,
|
TotalScore = 123456,
|
||||||
Accuracy = 0.6543,
|
Accuracy = 0.6543,
|
||||||
},
|
},
|
||||||
new APIScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
@ -241,7 +240,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
TotalScore = 12345678,
|
TotalScore = 12345678,
|
||||||
Accuracy = 0.9854,
|
Accuracy = 0.9854,
|
||||||
},
|
},
|
||||||
new APIScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
@ -269,7 +268,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
|
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
|
||||||
}
|
}
|
||||||
|
|
||||||
var topScoreInfo = new APIScoreInfo
|
var topScoreInfo = new ScoreInfo
|
||||||
{
|
{
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -38,6 +39,10 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
this.storyboard = storyboard;
|
this.storyboard = storyboard;
|
||||||
base.ParseStreamInto(stream, storyboard);
|
base.ParseStreamInto(stream, storyboard);
|
||||||
|
|
||||||
|
// OrderBy is used to guarantee that the parsing order of elements with equal start times is maintained (stably-sorted)
|
||||||
|
foreach (StoryboardLayer layer in storyboard.Layers)
|
||||||
|
layer.Elements = layer.Elements.OrderBy(h => h.StartTime).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ParseLine(Storyboard storyboard, Section section, string line)
|
protected override void ParseLine(Storyboard storyboard, Section section, string line)
|
||||||
|
@ -10,7 +10,7 @@ using osu.Game.Online.API.Requests.Responses;
|
|||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
{
|
{
|
||||||
public class GetScoresRequest : APIRequest<APIScores>
|
public class GetScoresRequest : APIRequest<APILegacyScores>
|
||||||
{
|
{
|
||||||
private readonly BeatmapInfo beatmap;
|
private readonly BeatmapInfo beatmap;
|
||||||
private readonly BeatmapLeaderboardScope scope;
|
private readonly BeatmapLeaderboardScope scope;
|
||||||
@ -31,9 +31,9 @@ namespace osu.Game.Online.API.Requests
|
|||||||
Success += onSuccess;
|
Success += onSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSuccess(APIScores r)
|
private void onSuccess(APILegacyScores r)
|
||||||
{
|
{
|
||||||
foreach (APIScoreInfo score in r.Scores)
|
foreach (APILegacyScoreInfo score in r.Scores)
|
||||||
score.Beatmap = beatmap;
|
score.Beatmap = beatmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ using osu.Game.Online.API.Requests.Responses;
|
|||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
{
|
{
|
||||||
public class GetUserScoresRequest : APIRequest<List<APIScoreInfo>>
|
public class GetUserScoresRequest : APIRequest<List<APILegacyScoreInfo>>
|
||||||
{
|
{
|
||||||
private readonly long userId;
|
private readonly long userId;
|
||||||
private readonly ScoreType type;
|
private readonly ScoreType type;
|
||||||
|
@ -8,12 +8,12 @@ using Newtonsoft.Json;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring.Legacy;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Online.API.Requests.Responses
|
namespace osu.Game.Online.API.Requests.Responses
|
||||||
{
|
{
|
||||||
public class APIScoreInfo : ScoreInfo
|
public class APILegacyScoreInfo : LegacyScoreInfo
|
||||||
{
|
{
|
||||||
[JsonProperty(@"score")]
|
[JsonProperty(@"score")]
|
||||||
private int totalScore
|
private int totalScore
|
||||||
@ -74,29 +74,37 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
HitResult newKey;
|
HitResult newKey;
|
||||||
switch (kvp.Key)
|
switch (kvp.Key)
|
||||||
{
|
{
|
||||||
|
case @"count_geki":
|
||||||
|
CountGeki = kvp.Value;
|
||||||
|
break;
|
||||||
case @"count_300":
|
case @"count_300":
|
||||||
newKey = HitResult.Great;
|
Count300 = kvp.Value;
|
||||||
|
break;
|
||||||
|
case @"count_katu":
|
||||||
|
CountKatu = kvp.Value;
|
||||||
break;
|
break;
|
||||||
case @"count_100":
|
case @"count_100":
|
||||||
newKey = HitResult.Good;
|
Count100 = kvp.Value;
|
||||||
break;
|
break;
|
||||||
case @"count_50":
|
case @"count_50":
|
||||||
newKey = HitResult.Meh;
|
Count50 = kvp.Value;
|
||||||
break;
|
break;
|
||||||
case @"count_miss":
|
case @"count_miss":
|
||||||
newKey = HitResult.Miss;
|
CountMiss = kvp.Value;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Statistics.Add(newKey, kvp.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty(@"mode_int")]
|
[JsonProperty(@"mode_int")]
|
||||||
public int OnlineRulesetID { get; set; }
|
public int OnlineRulesetID
|
||||||
|
{
|
||||||
|
get => RulesetID;
|
||||||
|
set => RulesetID = value;
|
||||||
|
}
|
||||||
|
|
||||||
[JsonProperty(@"mods")]
|
[JsonProperty(@"mods")]
|
||||||
private string[] modStrings { get; set; }
|
private string[] modStrings { get; set; }
|
@ -6,9 +6,9 @@ using Newtonsoft.Json;
|
|||||||
|
|
||||||
namespace osu.Game.Online.API.Requests.Responses
|
namespace osu.Game.Online.API.Requests.Responses
|
||||||
{
|
{
|
||||||
public class APIScores
|
public class APILegacyScores
|
||||||
{
|
{
|
||||||
[JsonProperty(@"scores")]
|
[JsonProperty(@"scores")]
|
||||||
public IEnumerable<APIScoreInfo> Scores;
|
public IEnumerable<APILegacyScoreInfo> Scores;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,7 +15,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Game.Overlays.Toolbar;
|
using osu.Game.Overlays.Toolbar;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osuTK;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -442,7 +441,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
loadComponentSingleFile(musicController = new MusicController
|
loadComponentSingleFile(musicController = new MusicController
|
||||||
{
|
{
|
||||||
Position = new Vector2(0, Toolbar.HEIGHT),
|
GetToolbarHeight = () => ToolbarOffset,
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
}, floatingOverlayContent.Add);
|
}, floatingOverlayContent.Add);
|
||||||
|
@ -9,12 +9,12 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
|
||||||
using osu.Game.Online.Leaderboards;
|
using osu.Game.Online.Leaderboards;
|
||||||
using osu.Game.Overlays.Profile.Sections.Ranks;
|
using osu.Game.Overlays.Profile.Sections.Ranks;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.BeatmapSet.Scores
|
namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||||
@ -26,7 +26,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
|
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
|
|
||||||
public DrawableScore(int index, APIScoreInfo score)
|
public DrawableScore(int index, ScoreInfo score)
|
||||||
{
|
{
|
||||||
ScoreModsContainer modsContainer;
|
ScoreModsContainer modsContainer;
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
|
||||||
using osu.Game.Online.Leaderboards;
|
using osu.Game.Online.Leaderboards;
|
||||||
using osu.Game.Overlays.Profile.Sections.Ranks;
|
using osu.Game.Overlays.Profile.Sections.Ranks;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -43,9 +42,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
private readonly InfoColumn statistics;
|
private readonly InfoColumn statistics;
|
||||||
private readonly ScoreModsContainer modsContainer;
|
private readonly ScoreModsContainer modsContainer;
|
||||||
|
|
||||||
private APIScoreInfo score;
|
private ScoreInfo score;
|
||||||
|
|
||||||
public APIScoreInfo Score
|
public ScoreInfo Score
|
||||||
{
|
{
|
||||||
get => score;
|
get => score;
|
||||||
set
|
set
|
||||||
|
@ -11,7 +11,7 @@ using System.Linq;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.BeatmapSet.Scores
|
namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||||
{
|
{
|
||||||
@ -29,10 +29,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration);
|
set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<APIScoreInfo> scores;
|
private IEnumerable<ScoreInfo> scores;
|
||||||
private BeatmapInfo beatmap;
|
private BeatmapInfo beatmap;
|
||||||
|
|
||||||
public IEnumerable<APIScoreInfo> Scores
|
public IEnumerable<ScoreInfo> Scores
|
||||||
{
|
{
|
||||||
get => scores;
|
get => scores;
|
||||||
set
|
set
|
||||||
|
@ -56,6 +56,11 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provide a source for the toolbar height.
|
||||||
|
/// </summary>
|
||||||
|
public Func<float> GetToolbarHeight;
|
||||||
|
|
||||||
public MusicController()
|
public MusicController()
|
||||||
{
|
{
|
||||||
Width = 400;
|
Width = 400;
|
||||||
@ -244,6 +249,8 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
base.UpdateAfterChildren();
|
base.UpdateAfterChildren();
|
||||||
Height = dragContainer.Height;
|
Height = dragContainer.Height;
|
||||||
|
|
||||||
|
dragContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
processor?.PostProcess();
|
processor?.PostProcess();
|
||||||
|
|
||||||
// Add visual representation
|
// Add visual representation
|
||||||
var drawableObject = drawableRuleset.GetVisualRepresentation(tObject);
|
var drawableObject = drawableRuleset.CreateDrawableRepresentation(tObject);
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(drawableObject);
|
drawableRuleset.Playfield.Add(drawableObject);
|
||||||
drawableRuleset.Playfield.PostProcess();
|
drawableRuleset.Playfield.PostProcess();
|
||||||
|
@ -7,7 +7,6 @@ using osu.Framework.Input.StateChanges;
|
|||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Replays
|
namespace osu.Game.Rulesets.Replays
|
||||||
{
|
{
|
||||||
@ -22,12 +21,37 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
|
|
||||||
protected List<ReplayFrame> Frames => replay.Frames;
|
protected List<ReplayFrame> Frames => replay.Frames;
|
||||||
|
|
||||||
public TFrame CurrentFrame => !HasFrames ? null : (TFrame)Frames[currentFrameIndex];
|
public TFrame CurrentFrame
|
||||||
public TFrame NextFrame => !HasFrames ? null : (TFrame)Frames[nextFrameIndex];
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!HasFrames || !currentFrameIndex.HasValue)
|
||||||
|
return null;
|
||||||
|
|
||||||
private int currentFrameIndex;
|
return (TFrame)Frames[currentFrameIndex.Value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1);
|
public TFrame NextFrame
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!HasFrames)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!currentFrameIndex.HasValue)
|
||||||
|
return (TFrame)Frames[0];
|
||||||
|
|
||||||
|
if (currentDirection > 0)
|
||||||
|
return currentFrameIndex == Frames.Count - 1 ? null : (TFrame)Frames[currentFrameIndex.Value + 1];
|
||||||
|
else
|
||||||
|
return currentFrameIndex == 0 ? null : (TFrame)Frames[nextFrameIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int? currentFrameIndex;
|
||||||
|
|
||||||
|
private int nextFrameIndex => currentFrameIndex.HasValue ? MathHelper.Clamp(currentFrameIndex.Value + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1) : 0;
|
||||||
|
|
||||||
protected FramedReplayInputHandler(Replay replay)
|
protected FramedReplayInputHandler(Replay replay)
|
||||||
{
|
{
|
||||||
@ -47,12 +71,12 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
|
|
||||||
public override List<IInput> GetPendingInputs() => new List<IInput>();
|
public override List<IInput> GetPendingInputs() => new List<IInput>();
|
||||||
|
|
||||||
public bool AtLastFrame => currentFrameIndex == Frames.Count - 1;
|
|
||||||
public bool AtFirstFrame => currentFrameIndex == 0;
|
|
||||||
|
|
||||||
private const double sixty_frame_time = 1000.0 / 60;
|
private const double sixty_frame_time = 1000.0 / 60;
|
||||||
|
|
||||||
protected double CurrentTime { get; private set; }
|
protected virtual double AllowedImportantTimeSpan => sixty_frame_time * 1.2;
|
||||||
|
|
||||||
|
protected double? CurrentTime { get; private set; }
|
||||||
|
|
||||||
private int currentDirection;
|
private int currentDirection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -68,7 +92,7 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
//a button is in a pressed state
|
//a button is in a pressed state
|
||||||
IsImportant(currentDirection > 0 ? CurrentFrame : NextFrame) &&
|
IsImportant(currentDirection > 0 ? CurrentFrame : NextFrame) &&
|
||||||
//the next frame is within an allowable time span
|
//the next frame is within an allowable time span
|
||||||
Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2;
|
Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= AllowedImportantTimeSpan;
|
||||||
|
|
||||||
protected virtual bool IsImportant(TFrame frame) => false;
|
protected virtual bool IsImportant(TFrame frame) => false;
|
||||||
|
|
||||||
@ -80,48 +104,37 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
/// <param name="time">The time which we should use for finding the current frame.</param>
|
/// <param name="time">The time which we should use for finding the current frame.</param>
|
||||||
/// <returns>The usable time value. If null, we should not advance time as we do not have enough data.</returns>
|
/// <returns>The usable time value. If null, we should not advance time as we do not have enough data.</returns>
|
||||||
public override double? SetFrameFromTime(double time)
|
public override double? SetFrameFromTime(double time)
|
||||||
|
{
|
||||||
|
if (!CurrentTime.HasValue)
|
||||||
|
{
|
||||||
|
currentDirection = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
currentDirection = time.CompareTo(CurrentTime);
|
currentDirection = time.CompareTo(CurrentTime);
|
||||||
if (currentDirection == 0) currentDirection = 1;
|
if (currentDirection == 0) currentDirection = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (HasFrames)
|
if (HasFrames)
|
||||||
{
|
{
|
||||||
// check if the next frame is in the "future" for the current playback direction
|
// check if the next frame is valid for the current playback direction.
|
||||||
if (currentDirection != time.CompareTo(NextFrame.Time))
|
// validity is if the next frame is equal or "earlier"
|
||||||
|
var compare = time.CompareTo(NextFrame?.Time);
|
||||||
|
|
||||||
|
if (compare == 0 || compare == currentDirection)
|
||||||
|
{
|
||||||
|
if (advanceFrame())
|
||||||
|
return CurrentTime = CurrentFrame.Time;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null.
|
// if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null.
|
||||||
if (inImportantSection)
|
if (inImportantSection)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else if (advanceFrame())
|
|
||||||
{
|
|
||||||
// If going backwards, we need to execute once _before_ the frame time to reverse any judgements
|
|
||||||
// that would occur as a result of this frame in forward playback
|
|
||||||
if (currentDirection == -1)
|
|
||||||
return CurrentTime = CurrentFrame.Time - 1;
|
|
||||||
|
|
||||||
return CurrentTime = CurrentFrame.Time;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CurrentTime = time;
|
return CurrentTime = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class ReplayMouseState : osu.Framework.Input.States.MouseState
|
|
||||||
{
|
|
||||||
public ReplayMouseState(Vector2 position)
|
|
||||||
{
|
|
||||||
Position = position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class ReplayKeyboardState : osu.Framework.Input.States.KeyboardState
|
|
||||||
{
|
|
||||||
public ReplayKeyboardState(List<Key> keys)
|
|
||||||
{
|
|
||||||
foreach (var key in keys)
|
|
||||||
Keys.Add(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
private void loadObjects()
|
private void loadObjects()
|
||||||
{
|
{
|
||||||
foreach (TObject h in Beatmap.HitObjects)
|
foreach (TObject h in Beatmap.HitObjects)
|
||||||
addRepresentation(h);
|
addHitObject(h);
|
||||||
|
|
||||||
Playfield.PostProcess();
|
Playfield.PostProcess();
|
||||||
|
|
||||||
@ -196,9 +196,9 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>.
|
/// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hitObject">The <see cref="TObject"/> to add the visual representation for.</param>
|
/// <param name="hitObject">The <see cref="TObject"/> to add the visual representation for.</param>
|
||||||
private void addRepresentation(TObject hitObject)
|
private void addHitObject(TObject hitObject)
|
||||||
{
|
{
|
||||||
var drawableObject = GetVisualRepresentation(hitObject);
|
var drawableObject = CreateDrawableRepresentation(hitObject);
|
||||||
|
|
||||||
if (drawableObject == null)
|
if (drawableObject == null)
|
||||||
return;
|
return;
|
||||||
@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="h">The HitObject to make drawable.</param>
|
/// <param name="h">The HitObject to make drawable.</param>
|
||||||
/// <returns>The DrawableHitObject.</returns>
|
/// <returns>The DrawableHitObject.</returns>
|
||||||
public abstract DrawableHitObject<TObject> GetVisualRepresentation(TObject h);
|
public abstract DrawableHitObject<TObject> CreateDrawableRepresentation(TObject h);
|
||||||
|
|
||||||
public void Attach(KeyCounterDisplay keyCounter) =>
|
public void Attach(KeyCounterDisplay keyCounter) =>
|
||||||
(KeyBindingInputManager as ICanAttachKeyCounter)?.Attach(keyCounter);
|
(KeyBindingInputManager as ICanAttachKeyCounter)?.Attach(keyCounter);
|
||||||
|
116
osu.Game/Scoring/Legacy/LegacyScoreInfo.cs
Normal file
116
osu.Game/Scoring/Legacy/LegacyScoreInfo.cs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// 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.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Scoring.Legacy
|
||||||
|
{
|
||||||
|
public class LegacyScoreInfo : ScoreInfo
|
||||||
|
{
|
||||||
|
private int countGeki;
|
||||||
|
|
||||||
|
public int CountGeki
|
||||||
|
{
|
||||||
|
get => countGeki;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
countGeki = value;
|
||||||
|
|
||||||
|
switch (Ruleset?.ID ?? RulesetID)
|
||||||
|
{
|
||||||
|
case 3:
|
||||||
|
Statistics[HitResult.Perfect] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int count300;
|
||||||
|
|
||||||
|
public int Count300
|
||||||
|
{
|
||||||
|
get => count300;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
count300 = value;
|
||||||
|
|
||||||
|
switch (Ruleset?.ID ?? RulesetID)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 3:
|
||||||
|
Statistics[HitResult.Great] = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Statistics[HitResult.Perfect] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int countKatu;
|
||||||
|
|
||||||
|
public int CountKatu
|
||||||
|
{
|
||||||
|
get => countKatu;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
countKatu = value;
|
||||||
|
|
||||||
|
switch (Ruleset?.ID ?? RulesetID)
|
||||||
|
{
|
||||||
|
case 3:
|
||||||
|
Statistics[HitResult.Good] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int count100;
|
||||||
|
|
||||||
|
public int Count100
|
||||||
|
{
|
||||||
|
get => count100;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
count100 = value;
|
||||||
|
|
||||||
|
switch (Ruleset?.ID ?? RulesetID)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
Statistics[HitResult.Good] = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
Statistics[HitResult.Ok] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int count50;
|
||||||
|
|
||||||
|
public int Count50
|
||||||
|
{
|
||||||
|
get => count50;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
count50 = value;
|
||||||
|
|
||||||
|
switch (Ruleset?.ID ?? RulesetID)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 3:
|
||||||
|
Statistics[HitResult.Meh] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CountMiss
|
||||||
|
{
|
||||||
|
get => Statistics[HitResult.Miss];
|
||||||
|
set => Statistics[HitResult.Miss] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,9 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
using (SerializationReader sr = new SerializationReader(stream))
|
using (SerializationReader sr = new SerializationReader(stream))
|
||||||
{
|
{
|
||||||
currentRuleset = GetRuleset(sr.ReadByte());
|
currentRuleset = GetRuleset(sr.ReadByte());
|
||||||
score.ScoreInfo = new ScoreInfo { Ruleset = currentRuleset.RulesetInfo };
|
var scoreInfo = new LegacyScoreInfo { Ruleset = currentRuleset.RulesetInfo };
|
||||||
|
|
||||||
|
score.ScoreInfo = scoreInfo;
|
||||||
|
|
||||||
var version = sr.ReadInt32();
|
var version = sr.ReadInt32();
|
||||||
|
|
||||||
@ -43,66 +45,39 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
throw new BeatmapNotFoundException();
|
throw new BeatmapNotFoundException();
|
||||||
|
|
||||||
currentBeatmap = workingBeatmap.Beatmap;
|
currentBeatmap = workingBeatmap.Beatmap;
|
||||||
score.ScoreInfo.Beatmap = currentBeatmap.BeatmapInfo;
|
scoreInfo.Beatmap = currentBeatmap.BeatmapInfo;
|
||||||
|
|
||||||
score.ScoreInfo.User = new User { Username = sr.ReadString() };
|
scoreInfo.User = new User { Username = sr.ReadString() };
|
||||||
|
|
||||||
// MD5Hash
|
// MD5Hash
|
||||||
sr.ReadString();
|
sr.ReadString();
|
||||||
|
|
||||||
var count300 = (int)sr.ReadUInt16();
|
scoreInfo.Count300 = sr.ReadUInt16();
|
||||||
var count100 = (int)sr.ReadUInt16();
|
scoreInfo.Count100 = sr.ReadUInt16();
|
||||||
var count50 = (int)sr.ReadUInt16();
|
scoreInfo.Count50 = sr.ReadUInt16();
|
||||||
var countGeki = (int)sr.ReadUInt16();
|
scoreInfo.CountGeki = sr.ReadUInt16();
|
||||||
var countKatu = (int)sr.ReadUInt16();
|
scoreInfo.CountKatu = sr.ReadUInt16();
|
||||||
var countMiss = (int)sr.ReadUInt16();
|
scoreInfo.CountMiss = sr.ReadUInt16();
|
||||||
|
|
||||||
switch (currentRuleset.LegacyID)
|
scoreInfo.TotalScore = sr.ReadInt32();
|
||||||
{
|
scoreInfo.MaxCombo = sr.ReadUInt16();
|
||||||
case 0:
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Great] = count300;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Good] = count100;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Meh] = count50;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Miss] = countMiss;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Great] = count300;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Good] = count100;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Miss] = countMiss;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Perfect] = count300;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Miss] = countMiss;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Perfect] = countGeki;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Great] = count300;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Good] = countKatu;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Ok] = count100;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Meh] = count50;
|
|
||||||
score.ScoreInfo.Statistics[HitResult.Miss] = countMiss;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
score.ScoreInfo.TotalScore = sr.ReadInt32();
|
|
||||||
score.ScoreInfo.MaxCombo = sr.ReadUInt16();
|
|
||||||
|
|
||||||
/* score.Perfect = */
|
/* score.Perfect = */
|
||||||
sr.ReadBoolean();
|
sr.ReadBoolean();
|
||||||
|
|
||||||
score.ScoreInfo.Mods = currentRuleset.ConvertLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
|
scoreInfo.Mods = currentRuleset.ConvertLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
|
||||||
|
|
||||||
/* score.HpGraphString = */
|
/* score.HpGraphString = */
|
||||||
sr.ReadString();
|
sr.ReadString();
|
||||||
|
|
||||||
score.ScoreInfo.Date = sr.ReadDateTime();
|
scoreInfo.Date = sr.ReadDateTime();
|
||||||
|
|
||||||
var compressedReplay = sr.ReadByteArray();
|
var compressedReplay = sr.ReadByteArray();
|
||||||
|
|
||||||
if (version >= 20140721)
|
if (version >= 20140721)
|
||||||
score.ScoreInfo.OnlineScoreID = sr.ReadInt64();
|
scoreInfo.OnlineScoreID = sr.ReadInt64();
|
||||||
else if (version >= 20121008)
|
else if (version >= 20121008)
|
||||||
score.ScoreInfo.OnlineScoreID = sr.ReadInt32();
|
scoreInfo.OnlineScoreID = sr.ReadInt32();
|
||||||
|
|
||||||
if (compressedReplay?.Length > 0)
|
if (compressedReplay?.Length > 0)
|
||||||
{
|
{
|
||||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly FramedOffsetClock offsetClock;
|
private readonly FramedOffsetClock offsetClock;
|
||||||
|
|
||||||
public GameplayClockContainer(WorkingBeatmap beatmap, bool allowLeadIn, double gameplayStartTime)
|
public GameplayClockContainer(WorkingBeatmap beatmap, double gameplayStartTime)
|
||||||
{
|
{
|
||||||
this.beatmap = beatmap;
|
this.beatmap = beatmap;
|
||||||
|
|
||||||
@ -64,9 +64,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||||
|
|
||||||
adjustableClock.Seek(allowLeadIn
|
adjustableClock.Seek(Math.Min(0, gameplayStartTime - beatmap.BeatmapInfo.AudioLeadIn));
|
||||||
? Math.Min(0, gameplayStartTime - beatmap.BeatmapInfo.AudioLeadIn)
|
|
||||||
: gameplayStartTime);
|
|
||||||
|
|
||||||
adjustableClock.ProcessFrame();
|
adjustableClock.ProcessFrame();
|
||||||
|
|
||||||
|
@ -43,10 +43,6 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public bool HasFailed { get; private set; }
|
public bool HasFailed { get; private set; }
|
||||||
|
|
||||||
public bool AllowPause { get; set; } = true;
|
|
||||||
public bool AllowLeadIn { get; set; } = true;
|
|
||||||
public bool AllowResults { get; set; } = true;
|
|
||||||
|
|
||||||
public bool PauseOnFocusLost { get; set; } = true;
|
public bool PauseOnFocusLost { get; set; } = true;
|
||||||
|
|
||||||
private Bindable<bool> mouseWheelDisabled;
|
private Bindable<bool> mouseWheelDisabled;
|
||||||
@ -73,6 +69,20 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
protected GameplayClockContainer GameplayClockContainer { get; private set; }
|
protected GameplayClockContainer GameplayClockContainer { get; private set; }
|
||||||
|
|
||||||
|
private readonly bool allowPause;
|
||||||
|
private readonly bool showResults;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new player instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="allowPause">Whether pausing should be allowed. If not allowed, attempting to pause will quit.</param>
|
||||||
|
/// <param name="showResults">Whether results screen should be pushed on completion.</param>
|
||||||
|
public Player(bool allowPause = true, bool showResults = true)
|
||||||
|
{
|
||||||
|
this.allowPause = allowPause;
|
||||||
|
this.showResults = showResults;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, IAPIProvider api, OsuConfigManager config)
|
private void load(AudioManager audio, IAPIProvider api, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
@ -92,7 +102,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (!ScoreProcessor.Mode.Disabled)
|
if (!ScoreProcessor.Mode.Disabled)
|
||||||
config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
|
config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
|
||||||
|
|
||||||
InternalChild = GameplayClockContainer = new GameplayClockContainer(working, AllowLeadIn, DrawableRuleset.GameplayStartTime);
|
InternalChild = GameplayClockContainer = new GameplayClockContainer(working, DrawableRuleset.GameplayStartTime);
|
||||||
|
|
||||||
GameplayClockContainer.Children = new[]
|
GameplayClockContainer.Children = new[]
|
||||||
{
|
{
|
||||||
@ -236,7 +246,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
ValidForResume = false;
|
ValidForResume = false;
|
||||||
|
|
||||||
if (!AllowResults) return;
|
if (!showResults) return;
|
||||||
|
|
||||||
using (BeginDelayedSequence(1000))
|
using (BeginDelayedSequence(1000))
|
||||||
{
|
{
|
||||||
@ -350,7 +360,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private bool canPause =>
|
private bool canPause =>
|
||||||
// must pass basic screen conditions (beatmap loaded, instance allows pause)
|
// must pass basic screen conditions (beatmap loaded, instance allows pause)
|
||||||
LoadedBeatmapSuccessfully && AllowPause && ValidForResume
|
LoadedBeatmapSuccessfully && allowPause && ValidForResume
|
||||||
// replays cannot be paused and exit immediately
|
// replays cannot be paused and exit immediately
|
||||||
&& !DrawableRuleset.HasReplayLoaded.Value
|
&& !DrawableRuleset.HasReplayLoaded.Value
|
||||||
// cannot pause if we are already in a fail state
|
// cannot pause if we are already in a fail state
|
||||||
|
@ -9,7 +9,8 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
private readonly Score score;
|
private readonly Score score;
|
||||||
|
|
||||||
public ReplayPlayer(Score score)
|
public ReplayPlayer(Score score, bool allowPause = true, bool showResults = true)
|
||||||
|
: base(allowPause, showResults)
|
||||||
{
|
{
|
||||||
this.score = score;
|
this.score = score;
|
||||||
}
|
}
|
||||||
|
@ -90,13 +90,12 @@ namespace osu.Game.Screens.Select
|
|||||||
protected SongSelect()
|
protected SongSelect()
|
||||||
{
|
{
|
||||||
const float carousel_width = 640;
|
const float carousel_width = 640;
|
||||||
const float filter_height = 100;
|
|
||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
new ParallaxContainer
|
new ParallaxContainer
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Top = filter_height },
|
Masking = true,
|
||||||
ParallaxAmount = 0.005f,
|
ParallaxAmount = 0.005f,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
@ -155,7 +154,7 @@ namespace osu.Game.Screens.Select
|
|||||||
FilterControl = new FilterControl
|
FilterControl = new FilterControl
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = filter_height,
|
Height = 100,
|
||||||
FilterChanged = c => Carousel.Filter(c),
|
FilterChanged = c => Carousel.Filter(c),
|
||||||
Background = { Width = 2 },
|
Background = { Width = 2 },
|
||||||
Exit = () =>
|
Exit = () =>
|
||||||
@ -414,7 +413,6 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\"");
|
Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\"");
|
||||||
|
|
||||||
preview = beatmap?.BeatmapSetInfoID != Beatmap.Value?.BeatmapInfo.BeatmapSetInfoID;
|
|
||||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
|
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
|
||||||
|
|
||||||
if (beatmap != null)
|
if (beatmap != null)
|
||||||
@ -426,7 +424,8 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.IsCurrentScreen()) ensurePlayingSelected(preview);
|
if (this.IsCurrentScreen())
|
||||||
|
ensurePlayingSelected();
|
||||||
UpdateBeatmap(Beatmap.Value);
|
UpdateBeatmap(Beatmap.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -577,17 +576,17 @@ namespace osu.Game.Screens.Select
|
|||||||
beatmap.Track.Looping = true;
|
beatmap.Track.Looping = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensurePlayingSelected(bool preview = false)
|
private void ensurePlayingSelected(bool restart = false)
|
||||||
{
|
{
|
||||||
Track track = Beatmap.Value.Track;
|
Track track = Beatmap.Value.Track;
|
||||||
|
|
||||||
if (!track.IsRunning)
|
if (!track.IsRunning || restart)
|
||||||
{
|
{
|
||||||
// Ensure the track is added to the TrackManager, since it is removed after the player finishes the map.
|
// Ensure the track is added to the TrackManager, since it is removed after the player finishes the map.
|
||||||
// Using AddItemToList rather than AddItem so that it doesn't attempt to register adjustment dependencies more than once.
|
// Using AddItemToList rather than AddItem so that it doesn't attempt to register adjustment dependencies more than once.
|
||||||
Game.Audio.Track.AddItemToList(track);
|
Game.Audio.Track.AddItemToList(track);
|
||||||
if (preview) track.Seek(Beatmap.Value.Metadata.PreviewTime);
|
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
|
||||||
track.Start();
|
track.Restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
public DrawableStoryboardSample(StoryboardSample sample)
|
public DrawableStoryboardSample(StoryboardSample sample)
|
||||||
{
|
{
|
||||||
this.sample = sample;
|
this.sample = sample;
|
||||||
LifetimeStart = sample.Time;
|
LifetimeStart = sample.StartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -43,27 +43,27 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
// TODO: this logic will need to be consolidated with other game samples like hit sounds.
|
// TODO: this logic will need to be consolidated with other game samples like hit sounds.
|
||||||
if (Time.Current < sample.Time)
|
if (Time.Current < sample.StartTime)
|
||||||
{
|
{
|
||||||
// We've rewound before the start time of the sample
|
// We've rewound before the start time of the sample
|
||||||
channel?.Stop();
|
channel?.Stop();
|
||||||
|
|
||||||
// In the case that the user fast-forwards to a point far beyond the start time of the sample,
|
// In the case that the user fast-forwards to a point far beyond the start time of the sample,
|
||||||
// we want to be able to fall into the if-conditional below (therefore we must not have a life time end)
|
// we want to be able to fall into the if-conditional below (therefore we must not have a life time end)
|
||||||
LifetimeStart = sample.Time;
|
LifetimeStart = sample.StartTime;
|
||||||
LifetimeEnd = double.MaxValue;
|
LifetimeEnd = double.MaxValue;
|
||||||
}
|
}
|
||||||
else if (Time.Current - Time.Elapsed < sample.Time)
|
else if (Time.Current - Time.Elapsed < sample.StartTime)
|
||||||
{
|
{
|
||||||
// We've passed the start time of the sample. We only play the sample if we're within an allowable range
|
// We've passed the start time of the sample. We only play the sample if we're within an allowable range
|
||||||
// from the sample's start, to reduce layering if we've been fast-forwarded far into the future
|
// from the sample's start, to reduce layering if we've been fast-forwarded far into the future
|
||||||
if (Time.Current - sample.Time < allowable_late_start)
|
if (Time.Current - sample.StartTime < allowable_late_start)
|
||||||
channel?.Play();
|
channel?.Play();
|
||||||
|
|
||||||
// In the case that the user rewinds to a point far behind the start time of the sample,
|
// In the case that the user rewinds to a point far behind the start time of the sample,
|
||||||
// we want to be able to fall into the if-conditional above (therefore we must not have a life time start)
|
// we want to be able to fall into the if-conditional above (therefore we must not have a life time start)
|
||||||
LifetimeStart = double.MinValue;
|
LifetimeStart = double.MinValue;
|
||||||
LifetimeEnd = sample.Time;
|
LifetimeEnd = sample.StartTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ namespace osu.Game.Storyboards
|
|||||||
string Path { get; }
|
string Path { get; }
|
||||||
bool IsDrawable { get; }
|
bool IsDrawable { get; }
|
||||||
|
|
||||||
|
double StartTime { get; }
|
||||||
|
|
||||||
Drawable CreateDrawable();
|
Drawable CreateDrawable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,7 @@ namespace osu.Game.Storyboards
|
|||||||
public bool EnabledWhenPassing = true;
|
public bool EnabledWhenPassing = true;
|
||||||
public bool EnabledWhenFailing = true;
|
public bool EnabledWhenFailing = true;
|
||||||
|
|
||||||
private readonly List<IStoryboardElement> elements = new List<IStoryboardElement>();
|
public List<IStoryboardElement> Elements = new List<IStoryboardElement>();
|
||||||
public IEnumerable<IStoryboardElement> Elements => elements;
|
|
||||||
|
|
||||||
public StoryboardLayer(string name, int depth)
|
public StoryboardLayer(string name, int depth)
|
||||||
{
|
{
|
||||||
@ -24,7 +23,7 @@ namespace osu.Game.Storyboards
|
|||||||
|
|
||||||
public void Add(IStoryboardElement element)
|
public void Add(IStoryboardElement element)
|
||||||
{
|
{
|
||||||
elements.Add(element);
|
Elements.Add(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DrawableStoryboardLayer CreateDrawable()
|
public DrawableStoryboardLayer CreateDrawable()
|
||||||
|
@ -11,13 +11,14 @@ namespace osu.Game.Storyboards
|
|||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public bool IsDrawable => true;
|
public bool IsDrawable => true;
|
||||||
|
|
||||||
public double Time;
|
public double StartTime { get; }
|
||||||
|
|
||||||
public float Volume;
|
public float Volume;
|
||||||
|
|
||||||
public StoryboardSample(string path, double time, float volume)
|
public StoryboardSample(string path, double time, float volume)
|
||||||
{
|
{
|
||||||
Path = path;
|
Path = path;
|
||||||
Time = time;
|
StartTime = time;
|
||||||
Volume = volume;
|
Volume = volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,11 +80,6 @@ namespace osu.Game.Tests.Visual
|
|||||||
return Player;
|
return Player;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Player CreatePlayer(Ruleset ruleset) => new Player
|
protected virtual Player CreatePlayer(Ruleset ruleset) => new Player(false, false);
|
||||||
{
|
|
||||||
AllowPause = false,
|
|
||||||
AllowLeadIn = false,
|
|
||||||
AllowResults = false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,11 +61,6 @@ namespace osu.Game.Tests.Visual
|
|||||||
LoadScreen(Player);
|
LoadScreen(Player);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Player CreatePlayer(Ruleset ruleset) => new Player
|
protected virtual Player CreatePlayer(Ruleset ruleset) => new Player(false, false);
|
||||||
{
|
|
||||||
AllowPause = false,
|
|
||||||
AllowLeadIn = false,
|
|
||||||
AllowResults = false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user