mirror of
https://github.com/ppy/osu.git
synced 2025-01-27 15:53:19 +08:00
Merge branch 'master' into spectator-state-rework
This commit is contained in:
commit
6d3bc005ea
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Replays
|
|||||||
|
|
||||||
protected override bool IsImportant(EmptyFreeformReplayFrame frame) => frame.Actions.Any();
|
protected override bool IsImportant(EmptyFreeformReplayFrame frame) => frame.Actions.Any();
|
||||||
|
|
||||||
public override void CollectPendingInputs(List<IInput> inputs)
|
protected override void CollectReplayInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
var position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time);
|
var position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time);
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Pippidon.Replays
|
|||||||
|
|
||||||
protected override bool IsImportant(PippidonReplayFrame frame) => true;
|
protected override bool IsImportant(PippidonReplayFrame frame) => true;
|
||||||
|
|
||||||
public override void CollectPendingInputs(List<IInput> inputs)
|
protected override void CollectReplayInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
var position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time);
|
var position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time);
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Replays
|
|||||||
|
|
||||||
protected override bool IsImportant(EmptyScrollingReplayFrame frame) => frame.Actions.Any();
|
protected override bool IsImportant(EmptyScrollingReplayFrame frame) => frame.Actions.Any();
|
||||||
|
|
||||||
public override void CollectPendingInputs(List<IInput> inputs)
|
protected override void CollectReplayInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
inputs.Add(new ReplayState<EmptyScrollingAction>
|
inputs.Add(new ReplayState<EmptyScrollingAction>
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Pippidon.Replays
|
|||||||
|
|
||||||
protected override bool IsImportant(PippidonReplayFrame frame) => frame.Actions.Any();
|
protected override bool IsImportant(PippidonReplayFrame frame) => frame.Actions.Any();
|
||||||
|
|
||||||
public override void CollectPendingInputs(List<IInput> inputs)
|
protected override void CollectReplayInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
inputs.Add(new ReplayState<PippidonAction>
|
inputs.Add(new ReplayState<PippidonAction>
|
||||||
{
|
{
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.115.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.202.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.128.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.128.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
|
|
||||||
protected override bool IsImportant(CatchReplayFrame frame) => frame.Actions.Any();
|
protected override bool IsImportant(CatchReplayFrame frame) => frame.Actions.Any();
|
||||||
|
|
||||||
public override void CollectPendingInputs(List<IInput> inputs)
|
protected override void CollectReplayInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
float position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time);
|
float position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time);
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
|
|
||||||
case CatchSkinComponents.CatchComboCounter:
|
case CatchSkinComponents.CatchComboCounter:
|
||||||
if (providesComboCounter)
|
if (providesComboCounter)
|
||||||
return new LegacyCatchComboCounter(Skin);
|
return new LegacyCatchComboCounter();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
|
|
||||||
private readonly LegacyRollingCounter explosion;
|
private readonly LegacyRollingCounter explosion;
|
||||||
|
|
||||||
public LegacyCatchComboCounter(ISkin skin)
|
public LegacyCatchComboCounter()
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
103
osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHoldOff.cs
Normal file
103
osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModHoldOff.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||||
|
{
|
||||||
|
public class TestSceneManiaModHoldOff : ModTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMapHasNoHoldNotes()
|
||||||
|
{
|
||||||
|
var testBeatmap = createModdedBeatmap();
|
||||||
|
Assert.False(testBeatmap.HitObjects.OfType<HoldNote>().Any());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCorrectNoteValues()
|
||||||
|
{
|
||||||
|
var testBeatmap = createRawBeatmap();
|
||||||
|
var noteValues = new List<double>(testBeatmap.HitObjects.OfType<HoldNote>().Count());
|
||||||
|
|
||||||
|
foreach (HoldNote h in testBeatmap.HitObjects.OfType<HoldNote>())
|
||||||
|
{
|
||||||
|
noteValues.Add(ManiaModHoldOff.GetNoteDurationInBeatLength(h, testBeatmap));
|
||||||
|
}
|
||||||
|
|
||||||
|
noteValues.Sort();
|
||||||
|
Assert.AreEqual(noteValues, new List<double> { 0.125, 0.250, 0.500, 1.000, 2.000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCorrectObjectCount()
|
||||||
|
{
|
||||||
|
// Ensure that the mod produces the expected number of objects when applied.
|
||||||
|
|
||||||
|
var rawBeatmap = createRawBeatmap();
|
||||||
|
var testBeatmap = createModdedBeatmap();
|
||||||
|
|
||||||
|
// Calculate expected number of objects
|
||||||
|
int expectedObjectCount = 0;
|
||||||
|
|
||||||
|
foreach (ManiaHitObject h in rawBeatmap.HitObjects)
|
||||||
|
{
|
||||||
|
// Both notes and hold notes account for at least one object
|
||||||
|
expectedObjectCount++;
|
||||||
|
|
||||||
|
if (h.GetType() == typeof(HoldNote))
|
||||||
|
{
|
||||||
|
double noteValue = ManiaModHoldOff.GetNoteDurationInBeatLength((HoldNote)h, rawBeatmap);
|
||||||
|
|
||||||
|
if (noteValue >= ManiaModHoldOff.END_NOTE_ALLOW_THRESHOLD)
|
||||||
|
{
|
||||||
|
// Should generate an end note if it's longer than the minimum note value
|
||||||
|
expectedObjectCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(testBeatmap.HitObjects.Count == expectedObjectCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ManiaBeatmap createModdedBeatmap()
|
||||||
|
{
|
||||||
|
var beatmap = createRawBeatmap();
|
||||||
|
var holdOffMod = new ManiaModHoldOff();
|
||||||
|
|
||||||
|
foreach (var hitObject in beatmap.HitObjects)
|
||||||
|
hitObject.ApplyDefaults(beatmap.ControlPointInfo, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
holdOffMod.ApplyToBeatmap(beatmap);
|
||||||
|
|
||||||
|
return beatmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ManiaBeatmap createRawBeatmap()
|
||||||
|
{
|
||||||
|
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
|
||||||
|
beatmap.ControlPointInfo.Add(0.0, new TimingControlPoint { BeatLength = 1000 }); // Set BPM to 60
|
||||||
|
|
||||||
|
// Add test hit objects
|
||||||
|
beatmap.HitObjects.Add(new Note { StartTime = 4000 });
|
||||||
|
beatmap.HitObjects.Add(new Note { StartTime = 4500 });
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 0, EndTime = 125 }); // 1/8 note
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 0, EndTime = 250 }); // 1/4 note
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 0, EndTime = 500 }); // 1/2 note
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 0, EndTime = 1000 }); // 1/1 note
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 0, EndTime = 2000 }); // 2/1 note
|
||||||
|
|
||||||
|
return beatmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Width = 0.5f,
|
Width = 0.5f,
|
||||||
Child = new ColumnHitObjectArea(0, new HitObjectContainer())
|
Child = new ColumnHitObjectArea(new HitObjectContainer())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Width = 0.5f,
|
Width = 0.5f,
|
||||||
Child = new ColumnHitObjectArea(1, new HitObjectContainer())
|
Child = new ColumnHitObjectArea(new HitObjectContainer())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,8 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
new ManiaModDifficultyAdjust(),
|
new ManiaModDifficultyAdjust(),
|
||||||
new ManiaModClassic(),
|
new ManiaModClassic(),
|
||||||
new ManiaModInvert(),
|
new ManiaModInvert(),
|
||||||
new ManiaModConstantSpeed()
|
new ManiaModConstantSpeed(),
|
||||||
|
new ManiaModHoldOff()
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
|
72
osu.Game.Rulesets.Mania/Mods/ManiaModHoldOff.cs
Normal file
72
osu.Game.Rulesets.Mania/Mods/ManiaModHoldOff.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// 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.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public class ManiaModHoldOff : Mod, IApplicableAfterBeatmapConversion
|
||||||
|
{
|
||||||
|
public override string Name => "Hold Off";
|
||||||
|
|
||||||
|
public override string Acronym => "HO";
|
||||||
|
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
public override string Description => @"Replaces all hold notes with normal notes.";
|
||||||
|
|
||||||
|
public override IconUsage? Icon => FontAwesome.Solid.DotCircle;
|
||||||
|
|
||||||
|
public override ModType Type => ModType.Conversion;
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ManiaModInvert) };
|
||||||
|
|
||||||
|
public const double END_NOTE_ALLOW_THRESHOLD = 0.5;
|
||||||
|
|
||||||
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||||
|
|
||||||
|
var newObjects = new List<ManiaHitObject>();
|
||||||
|
|
||||||
|
foreach (var h in beatmap.HitObjects.OfType<HoldNote>())
|
||||||
|
{
|
||||||
|
// Add a note for the beginning of the hold note
|
||||||
|
newObjects.Add(new Note
|
||||||
|
{
|
||||||
|
Column = h.Column,
|
||||||
|
StartTime = h.StartTime,
|
||||||
|
Samples = h.GetNodeSamples(0)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Don't add an end note if the duration is shorter than the threshold
|
||||||
|
double noteValue = GetNoteDurationInBeatLength(h, maniaBeatmap); // 1/1, 1/2, 1/4, etc.
|
||||||
|
|
||||||
|
if (noteValue >= END_NOTE_ALLOW_THRESHOLD)
|
||||||
|
{
|
||||||
|
newObjects.Add(new Note
|
||||||
|
{
|
||||||
|
Column = h.Column,
|
||||||
|
StartTime = h.EndTime,
|
||||||
|
Samples = h.GetNodeSamples((h.NodeSamples?.Count - 1) ?? 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maniaBeatmap.HitObjects = maniaBeatmap.HitObjects.OfType<Note>().Concat(newObjects).OrderBy(h => h.StartTime).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double GetNoteDurationInBeatLength(HoldNote holdNote, ManiaBeatmap beatmap)
|
||||||
|
{
|
||||||
|
double beatLength = beatmap.ControlPointInfo.TimingPointAt(holdNote.StartTime).BeatLength;
|
||||||
|
return holdNote.Duration / beatLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
|
|
||||||
public override ModType Type => ModType.Conversion;
|
public override ModType Type => ModType.Conversion;
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ManiaModHoldOff) };
|
||||||
|
|
||||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||||
|
@ -18,7 +18,7 @@ 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 void CollectPendingInputs(List<IInput> inputs)
|
protected override void CollectReplayInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
inputs.Add(new ReplayState<ManiaAction> { PressedActions = CurrentFrame?.Actions ?? new List<ManiaAction>() });
|
inputs.Add(new ReplayState<ManiaAction> { PressedActions = CurrentFrame?.Actions ?? new List<ManiaAction>() });
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
sampleTriggerSource = new GameplaySampleTriggerSource(HitObjectContainer),
|
sampleTriggerSource = new GameplaySampleTriggerSource(HitObjectContainer),
|
||||||
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
|
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
|
||||||
background.CreateProxy(),
|
background.CreateProxy(),
|
||||||
HitObjectArea = new ColumnHitObjectArea(Index, HitObjectContainer) { RelativeSizeAxes = Axes.Both },
|
HitObjectArea = new ColumnHitObjectArea(HitObjectContainer) { RelativeSizeAxes = Axes.Both },
|
||||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
|
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
|
|
||||||
private readonly Drawable hitTarget;
|
private readonly Drawable hitTarget;
|
||||||
|
|
||||||
public ColumnHitObjectArea(int columnIndex, HitObjectContainer hitObjectContainer)
|
public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
|
||||||
: base(hitObjectContainer)
|
: base(hitObjectContainer)
|
||||||
{
|
{
|
||||||
AddRangeInternal(new[]
|
AddRangeInternal(new[]
|
||||||
|
@ -118,7 +118,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
public Drawable GetDrawableComponent(ISkinComponent component) => null;
|
public Drawable GetDrawableComponent(ISkinComponent component) => null;
|
||||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
||||||
public ISample GetSample(ISampleInfo sampleInfo) => null;
|
public ISample GetSample(ISampleInfo sampleInfo) => null;
|
||||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
|
|
||||||
protected override bool IsImportant(OsuReplayFrame frame) => frame.Actions.Any();
|
protected override bool IsImportant(OsuReplayFrame frame) => frame.Actions.Any();
|
||||||
|
|
||||||
public override void CollectPendingInputs(List<IInput> inputs)
|
protected override void CollectReplayInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
var position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time);
|
var position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time);
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ 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 void CollectPendingInputs(List<IInput> inputs)
|
protected override void CollectReplayInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
inputs.Add(new ReplayState<TaikoAction> { PressedActions = CurrentFrame?.Actions ?? new List<TaikoAction>() });
|
inputs.Add(new ReplayState<TaikoAction> { PressedActions = CurrentFrame?.Actions ?? new List<TaikoAction>() });
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
beatmap.BeatmapInfo.BeatmapSet.Files.Add(CheckTestHelpers.CreateMockFile("jpg"));
|
beatmap.BeatmapInfo.BeatmapSet.Files.Add(CheckTestHelpers.CreateMockFile("jpg"));
|
||||||
|
|
||||||
// Should fail to load, but not produce an error due to the extension not being expected to load.
|
// Should fail to load, but not produce an error due to the extension not being expected to load.
|
||||||
Assert.IsEmpty(check.Run(getContext(null, allowMissing: true)));
|
Assert.IsEmpty(check.Run(getContext(null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -91,7 +91,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
{
|
{
|
||||||
using (var resourceStream = TestResources.OpenResource("Samples/missing.mp3"))
|
using (var resourceStream = TestResources.OpenResource("Samples/missing.mp3"))
|
||||||
{
|
{
|
||||||
Assert.IsEmpty(check.Run(getContext(resourceStream, allowMissing: true)));
|
Assert.IsEmpty(check.Run(getContext(resourceStream)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ namespace osu.Game.Tests.Editing.Checks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BeatmapVerifierContext getContext(Stream resourceStream, bool allowMissing = false)
|
private BeatmapVerifierContext getContext(Stream resourceStream)
|
||||||
{
|
{
|
||||||
var mockWorkingBeatmap = new Mock<TestWorkingBeatmap>(beatmap, null, null);
|
var mockWorkingBeatmap = new Mock<TestWorkingBeatmap>(beatmap, null, null);
|
||||||
mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny<string>())).Returns(resourceStream);
|
mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny<string>())).Returns(resourceStream);
|
||||||
|
@ -131,8 +131,6 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
|
|
||||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||||
|
|
||||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
{
|
{
|
||||||
switch (lookup)
|
switch (lookup)
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
// 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;
|
||||||
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Online.Spectator;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
@ -42,6 +48,43 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(Judgement.LARGE_BONUS_SCORE));
|
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(Judgement.LARGE_BONUS_SCORE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResetFromReplayFrame()
|
||||||
|
{
|
||||||
|
var beatmap = new Beatmap<HitObject> { HitObjects = { new HitCircle() } };
|
||||||
|
|
||||||
|
var scoreProcessor = new ScoreProcessor();
|
||||||
|
scoreProcessor.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new TestJudgement(HitResult.Great)) { Type = HitResult.Great });
|
||||||
|
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
||||||
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
|
||||||
|
// No header shouldn't cause any change
|
||||||
|
scoreProcessor.ResetFromReplayFrame(new OsuRuleset(), new OsuReplayFrame());
|
||||||
|
|
||||||
|
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
||||||
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
|
||||||
|
// Reset with a miss instead.
|
||||||
|
scoreProcessor.ResetFromReplayFrame(new OsuRuleset(), new OsuReplayFrame
|
||||||
|
{
|
||||||
|
Header = new FrameHeader(0, 0, 0, new Dictionary<HitResult, int> { { HitResult.Miss, 1 } }, DateTimeOffset.Now)
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
||||||
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
|
||||||
|
// Reset with no judged hit.
|
||||||
|
scoreProcessor.ResetFromReplayFrame(new OsuRuleset(), new OsuReplayFrame
|
||||||
|
{
|
||||||
|
Header = new FrameHeader(0, 0, 0, new Dictionary<HitResult, int>(), DateTimeOffset.Now)
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
||||||
|
Assert.That(scoreProcessor.JudgedHits, Is.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
private class TestJudgement : Judgement
|
private class TestJudgement : Judgement
|
||||||
{
|
{
|
||||||
public override HitResult MaxResult { get; }
|
public override HitResult MaxResult { get; }
|
||||||
|
@ -61,7 +61,6 @@ namespace osu.Game.Tests.NonVisual.Skinning
|
|||||||
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
|
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
|
||||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotSupportedException();
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotSupportedException();
|
||||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestAnimationTimeReference : IAnimationTimeReference
|
private class TestAnimationTimeReference : IAnimationTimeReference
|
||||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
||||||
Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.CacheAs<BeatmapModelDownloader>(beatmapDownloader = new TestBeatmapModelDownloader(beatmaps, API, host));
|
Dependencies.CacheAs<BeatmapModelDownloader>(beatmapDownloader = new TestBeatmapModelDownloader(beatmaps, API));
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
@ -173,14 +173,14 @@ namespace osu.Game.Tests.Online
|
|||||||
|
|
||||||
protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue)
|
protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue)
|
||||||
{
|
{
|
||||||
return new TestBeatmapModelManager(this, storage, realm, rulesets, onlineLookupQueue);
|
return new TestBeatmapModelManager(this, storage, realm, onlineLookupQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class TestBeatmapModelManager : BeatmapModelManager
|
internal class TestBeatmapModelManager : BeatmapModelManager
|
||||||
{
|
{
|
||||||
private readonly TestBeatmapManager testBeatmapManager;
|
private readonly TestBeatmapManager testBeatmapManager;
|
||||||
|
|
||||||
public TestBeatmapModelManager(TestBeatmapManager testBeatmapManager, Storage storage, RealmAccess databaseAccess, RulesetStore rulesetStore, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue)
|
public TestBeatmapModelManager(TestBeatmapManager testBeatmapManager, Storage storage, RealmAccess databaseAccess, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue)
|
||||||
: base(databaseAccess, storage, beatmapOnlineLookupQueue)
|
: base(databaseAccess, storage, beatmapOnlineLookupQueue)
|
||||||
{
|
{
|
||||||
this.testBeatmapManager = testBeatmapManager;
|
this.testBeatmapManager = testBeatmapManager;
|
||||||
@ -196,7 +196,7 @@ namespace osu.Game.Tests.Online
|
|||||||
|
|
||||||
internal class TestBeatmapModelDownloader : BeatmapModelDownloader
|
internal class TestBeatmapModelDownloader : BeatmapModelDownloader
|
||||||
{
|
{
|
||||||
public TestBeatmapModelDownloader(IModelImporter<BeatmapSetInfo> importer, IAPIProvider apiProvider, GameHost gameHost)
|
public TestBeatmapModelDownloader(IModelImporter<BeatmapSetInfo> importer, IAPIProvider apiProvider)
|
||||||
: base(importer, apiProvider)
|
: base(importer, apiProvider)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -210,7 +210,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void CollectPendingInputs(List<IInput> inputs)
|
protected override void CollectReplayInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
inputs.Add(new MousePositionAbsoluteInput { Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero) });
|
inputs.Add(new MousePositionAbsoluteInput { Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero) });
|
||||||
inputs.Add(new ReplayState<TestAction> { PressedActions = CurrentFrame?.Actions ?? new List<TestAction>() });
|
inputs.Add(new ReplayState<TestAction> { PressedActions = CurrentFrame?.Actions ?? new List<TestAction>() });
|
||||||
|
@ -301,8 +301,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||||
|
|
||||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SecondarySource : ISkin
|
private class SecondarySource : ISkin
|
||||||
@ -314,8 +312,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||||
|
|
||||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cached(typeof(ISkinSource))]
|
[Cached(typeof(ISkinSource))]
|
||||||
|
@ -218,6 +218,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddAssert("last received frame has time = 1000", () => spectatorClient.LastReceivedUserFrames.First().Value.Time == 1000);
|
AddAssert("last received frame has time = 1000", () => spectatorClient.LastReceivedUserFrames.First().Value.Time == 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFinalFrameInBundleHasHeader()
|
||||||
|
{
|
||||||
|
FrameDataBundle lastBundle = null;
|
||||||
|
|
||||||
|
AddStep("bind to client", () => spectatorClient.OnNewFrames += (_, bundle) => lastBundle = bundle);
|
||||||
|
|
||||||
|
start(-1234);
|
||||||
|
sendFrames();
|
||||||
|
finish();
|
||||||
|
|
||||||
|
AddUntilStep("bundle received", () => lastBundle != null);
|
||||||
|
AddAssert("first frame does not have header", () => lastBundle.Frames[0].Header == null);
|
||||||
|
AddAssert("last frame has header", () => lastBundle.Frames[^1].Header != null);
|
||||||
|
}
|
||||||
|
|
||||||
private OsuFramedReplayInputHandler replayHandler =>
|
private OsuFramedReplayInputHandler replayHandler =>
|
||||||
(OsuFramedReplayInputHandler)Stack.ChildrenOfType<OsuInputManager>().First().ReplayInputHandler;
|
(OsuFramedReplayInputHandler)Stack.ChildrenOfType<OsuInputManager>().First().ReplayInputHandler;
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void CollectPendingInputs(List<IInput> inputs)
|
protected override void CollectReplayInputs(List<IInput> inputs)
|
||||||
{
|
{
|
||||||
inputs.Add(new MousePositionAbsoluteInput { Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero) });
|
inputs.Add(new MousePositionAbsoluteInput { Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero) });
|
||||||
inputs.Add(new ReplayState<TestAction> { PressedActions = CurrentFrame?.Actions ?? new List<TestAction>() });
|
inputs.Add(new ReplayState<TestAction> { PressedActions = CurrentFrame?.Actions ?? new List<TestAction>() });
|
||||||
|
@ -197,7 +197,24 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 1,
|
Accuracy = 1,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new Mod[]
|
||||||
|
{
|
||||||
|
new OsuModHidden(),
|
||||||
|
new OsuModHardRock(),
|
||||||
|
new OsuModFlashlight
|
||||||
|
{
|
||||||
|
FollowDelay = { Value = 200 },
|
||||||
|
SizeMultiplier = { Value = 5 },
|
||||||
|
},
|
||||||
|
new OsuModDifficultyAdjust
|
||||||
|
{
|
||||||
|
CircleSize = { Value = 11 },
|
||||||
|
ApproachRate = { Value = 10 },
|
||||||
|
OverallDifficulty = { Value = 10 },
|
||||||
|
DrainRate = { Value = 10 },
|
||||||
|
ExtendedLimits = { Value = true }
|
||||||
|
}
|
||||||
|
},
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
BeatmapInfo = beatmapInfo,
|
BeatmapInfo = beatmapInfo,
|
||||||
User = new APIUser
|
User = new APIUser
|
||||||
@ -217,7 +234,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 1,
|
Accuracy = 1,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
BeatmapInfo = beatmapInfo,
|
BeatmapInfo = beatmapInfo,
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
User = new APIUser
|
User = new APIUser
|
||||||
@ -237,7 +254,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 1,
|
Accuracy = 1,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
BeatmapInfo = beatmapInfo,
|
BeatmapInfo = beatmapInfo,
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
|
||||||
@ -258,7 +275,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 1,
|
Accuracy = 1,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
BeatmapInfo = beatmapInfo,
|
BeatmapInfo = beatmapInfo,
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
|
||||||
@ -279,7 +296,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 1,
|
Accuracy = 1,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
BeatmapInfo = beatmapInfo,
|
BeatmapInfo = beatmapInfo,
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
|
||||||
@ -300,7 +317,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 0.9826,
|
Accuracy = 0.9826,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
BeatmapInfo = beatmapInfo,
|
BeatmapInfo = beatmapInfo,
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
|
||||||
@ -321,7 +338,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 0.9654,
|
Accuracy = 0.9654,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
BeatmapInfo = beatmapInfo,
|
BeatmapInfo = beatmapInfo,
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
|
||||||
@ -342,7 +359,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 0.6025,
|
Accuracy = 0.6025,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
BeatmapInfo = beatmapInfo,
|
BeatmapInfo = beatmapInfo,
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
|
||||||
@ -363,7 +380,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 0.5140,
|
Accuracy = 0.5140,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
BeatmapInfo = beatmapInfo,
|
BeatmapInfo = beatmapInfo,
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
|
||||||
@ -384,7 +401,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 0.4222,
|
Accuracy = 0.4222,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
BeatmapInfo = beatmapInfo,
|
BeatmapInfo = beatmapInfo,
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public BeatmapMetadata(RealmUser? user = null)
|
public BeatmapMetadata(RealmUser? user = null)
|
||||||
{
|
{
|
||||||
Author = new RealmUser();
|
Author = user ?? new RealmUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UsedImplicitly] // Realm
|
[UsedImplicitly] // Realm
|
||||||
|
@ -30,6 +30,7 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
{
|
{
|
||||||
background = new Box
|
background = new Box
|
||||||
{
|
{
|
||||||
|
Alpha = 0.9f,
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Database
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
perform(retrieveFromID(r, ID));
|
perform(retrieveFromID(r));
|
||||||
RealmLiveStatistics.USAGE_ASYNC.Value++;
|
RealmLiveStatistics.USAGE_ASYNC.Value++;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
return realm.Run(r =>
|
return realm.Run(r =>
|
||||||
{
|
{
|
||||||
var returnData = perform(retrieveFromID(r, ID));
|
var returnData = perform(retrieveFromID(r));
|
||||||
RealmLiveStatistics.USAGE_ASYNC.Value++;
|
RealmLiveStatistics.USAGE_ASYNC.Value++;
|
||||||
|
|
||||||
if (returnData is RealmObjectBase realmObject && realmObject.IsManaged)
|
if (returnData is RealmObjectBase realmObject && realmObject.IsManaged)
|
||||||
@ -139,11 +139,11 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
dataIsFromUpdateThread = true;
|
dataIsFromUpdateThread = true;
|
||||||
data = retrieveFromID(realm.Realm, ID);
|
data = retrieveFromID(realm.Realm);
|
||||||
RealmLiveStatistics.USAGE_UPDATE_REFETCH.Value++;
|
RealmLiveStatistics.USAGE_UPDATE_REFETCH.Value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private T retrieveFromID(Realm realm, Guid id)
|
private T retrieveFromID(Realm realm)
|
||||||
{
|
{
|
||||||
var found = realm.Find<T>(ID);
|
var found = realm.Find<T>(ID);
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using System;
|
using System;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
@ -30,13 +32,17 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
private DragRotationState dragRotationState;
|
private DragRotationState dragRotationState;
|
||||||
private Vector2 positionMouseDown;
|
private Vector2 positionMouseDown;
|
||||||
|
|
||||||
|
private Sample tapSample;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager)
|
private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager, AudioManager audio)
|
||||||
{
|
{
|
||||||
cursorRotate = config.GetBindable<bool>(OsuSetting.CursorRotation);
|
cursorRotate = config.GetBindable<bool>(OsuSetting.CursorRotation);
|
||||||
|
|
||||||
if (screenshotManager != null)
|
if (screenshotManager != null)
|
||||||
screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility);
|
screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility);
|
||||||
|
|
||||||
|
tapSample = audio.Samples.Get(@"UI/cursor-tap");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
@ -87,6 +93,8 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
dragRotationState = DragRotationState.DragStarted;
|
dragRotationState = DragRotationState.DragStarted;
|
||||||
positionMouseDown = e.MousePosition;
|
positionMouseDown = e.MousePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playTapSample();
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnMouseDown(e);
|
return base.OnMouseDown(e);
|
||||||
@ -104,6 +112,9 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
activeCursor.RotateTo(0, 600 * (1 + Math.Abs(activeCursor.Rotation / 720)), Easing.OutElasticHalf);
|
activeCursor.RotateTo(0, 600 * (1 + Math.Abs(activeCursor.Rotation / 720)), Easing.OutElasticHalf);
|
||||||
dragRotationState = DragRotationState.NotDragging;
|
dragRotationState = DragRotationState.NotDragging;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (State.Value == Visibility.Visible)
|
||||||
|
playTapSample(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnMouseUp(e);
|
base.OnMouseUp(e);
|
||||||
@ -121,6 +132,18 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
activeCursor.ScaleTo(0.6f, 250, Easing.In);
|
activeCursor.ScaleTo(0.6f, 250, Easing.In);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void playTapSample(double baseFrequency = 1f)
|
||||||
|
{
|
||||||
|
const float random_range = 0.02f;
|
||||||
|
SampleChannel channel = tapSample.GetChannel();
|
||||||
|
|
||||||
|
// Scale to [-0.75, 0.75] so that the sample isn't fully panned left or right (sounds weird)
|
||||||
|
channel.Balance.Value = ((activeCursor.X / DrawWidth) * 2 - 1) * 0.75;
|
||||||
|
channel.Frequency.Value = baseFrequency - (random_range / 2f) + RNG.NextDouble(random_range);
|
||||||
|
|
||||||
|
channel.Play();
|
||||||
|
}
|
||||||
|
|
||||||
public class Cursor : Container
|
public class Cursor : Container
|
||||||
{
|
{
|
||||||
private Container cursorContainer;
|
private Container cursorContainer;
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Input.StateChanges;
|
|||||||
using osu.Framework.Input.StateChanges.Events;
|
using osu.Framework.Input.StateChanges.Events;
|
||||||
using osu.Framework.Input.States;
|
using osu.Framework.Input.States;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -79,5 +80,38 @@ namespace osu.Game.Input.Handlers
|
|||||||
PressedActions = pressedActions;
|
PressedActions = pressedActions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IInput"/> that is triggered when a frame containing replay statistics arrives.
|
||||||
|
/// </summary>
|
||||||
|
public class ReplayStatisticsFrameInput : IInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The frame containing the statistics.
|
||||||
|
/// </summary>
|
||||||
|
public ReplayFrame Frame;
|
||||||
|
|
||||||
|
public void Apply(InputState state, IInputStateChangeHandler handler)
|
||||||
|
{
|
||||||
|
handler.HandleInputStateChange(new ReplayStatisticsFrameEvent(state, this, Frame));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="InputStateChangeEvent"/> that is triggered when a frame containing replay statistics arrives.
|
||||||
|
/// </summary>
|
||||||
|
public class ReplayStatisticsFrameEvent : InputStateChangeEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The frame containing the statistics.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ReplayFrame Frame;
|
||||||
|
|
||||||
|
public ReplayStatisticsFrameEvent(InputState state, IInput input, ReplayFrame frame)
|
||||||
|
: base(state, input)
|
||||||
|
{
|
||||||
|
Frame = frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ using osu.Game.Utils;
|
|||||||
|
|
||||||
namespace osu.Game.Online.Leaderboards
|
namespace osu.Game.Online.Leaderboards
|
||||||
{
|
{
|
||||||
public class LeaderboardScore : OsuClickableContainer, IHasContextMenu
|
public class LeaderboardScore : OsuClickableContainer, IHasContextMenu, IHasCustomTooltip<ScoreInfo>
|
||||||
{
|
{
|
||||||
public const float HEIGHT = 60;
|
public const float HEIGHT = 60;
|
||||||
|
|
||||||
@ -70,6 +70,9 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private Storage storage { get; set; }
|
private Storage storage { get; set; }
|
||||||
|
|
||||||
|
public ITooltip<ScoreInfo> GetCustomTooltip() => new LeaderboardScoreTooltip();
|
||||||
|
public virtual ScoreInfo TooltipContent => Score;
|
||||||
|
|
||||||
public LeaderboardScore(ScoreInfo score, int? rank, bool isOnlineScope = true)
|
public LeaderboardScore(ScoreInfo score, int? rank, bool isOnlineScope = true)
|
||||||
{
|
{
|
||||||
Score = score;
|
Score = score;
|
||||||
@ -183,7 +186,6 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(10f, 0f),
|
|
||||||
Margin = new MarginPadding { Left = edge_margin },
|
Margin = new MarginPadding { Left = edge_margin },
|
||||||
Children = statisticsLabels
|
Children = statisticsLabels
|
||||||
},
|
},
|
||||||
@ -228,7 +230,6 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(1),
|
|
||||||
ChildrenEnumerable = Score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
|
ChildrenEnumerable = Score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -313,6 +314,7 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
|
Padding = new MarginPadding { Right = 10 },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
|
219
osu.Game/Online/Leaderboards/LeaderboardScoreTooltip.cs
Normal file
219
osu.Game/Online/Leaderboards/LeaderboardScoreTooltip.cs
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
// 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.Shapes;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace osu.Game.Online.Leaderboards
|
||||||
|
{
|
||||||
|
public class LeaderboardScoreTooltip : VisibilityContainer, ITooltip<ScoreInfo>
|
||||||
|
{
|
||||||
|
private OsuSpriteText timestampLabel = null!;
|
||||||
|
private FillFlowContainer<HitResultCell> topScoreStatistics = null!;
|
||||||
|
private FillFlowContainer<HitResultCell> bottomScoreStatistics = null!;
|
||||||
|
private FillFlowContainer<ModCell> modStatistics = null!;
|
||||||
|
|
||||||
|
public LeaderboardScoreTooltip()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
AutoSizeDuration = 200;
|
||||||
|
AutoSizeEasing = Easing.OutQuint;
|
||||||
|
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0.9f,
|
||||||
|
Colour = colours.Gray3,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding(5),
|
||||||
|
Spacing = new Vector2(10),
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
// Info row
|
||||||
|
timestampLabel = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
|
||||||
|
},
|
||||||
|
// Mods row
|
||||||
|
modStatistics = new FillFlowContainer<ModCell>
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(5, 0),
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
// Actual stats rows
|
||||||
|
topScoreStatistics = new FillFlowContainer<HitResultCell>
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(10, 0),
|
||||||
|
},
|
||||||
|
bottomScoreStatistics = new FillFlowContainer<HitResultCell>
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(10, 0),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScoreInfo? displayedScore;
|
||||||
|
|
||||||
|
public void SetContent(ScoreInfo score)
|
||||||
|
{
|
||||||
|
if (displayedScore?.Equals(score) == true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
displayedScore = score;
|
||||||
|
|
||||||
|
timestampLabel.Text = $"Played on {score.Date.ToLocalTime():d MMMM yyyy HH:mm}";
|
||||||
|
|
||||||
|
modStatistics.Clear();
|
||||||
|
topScoreStatistics.Clear();
|
||||||
|
bottomScoreStatistics.Clear();
|
||||||
|
|
||||||
|
foreach (var mod in score.Mods)
|
||||||
|
{
|
||||||
|
modStatistics.Add(new ModCell(mod));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var result in score.GetStatisticsForDisplay())
|
||||||
|
{
|
||||||
|
if (result.Result > HitResult.Perfect)
|
||||||
|
bottomScoreStatistics.Add(new HitResultCell(result));
|
||||||
|
else
|
||||||
|
topScoreStatistics.Add(new HitResultCell(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn() => this.FadeIn(20, Easing.OutQuint);
|
||||||
|
protected override void PopOut() => this.FadeOut(80, Easing.OutQuint);
|
||||||
|
|
||||||
|
public void Move(Vector2 pos) => Position = pos;
|
||||||
|
|
||||||
|
private class HitResultCell : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly string displayName;
|
||||||
|
private readonly HitResult result;
|
||||||
|
private readonly int count;
|
||||||
|
|
||||||
|
public HitResultCell(HitResultDisplayStatistic stat)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
displayName = stat.DisplayName;
|
||||||
|
result = stat.Result;
|
||||||
|
count = stat.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Height = 12,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(5f, 0f),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold),
|
||||||
|
Text = displayName.ToUpperInvariant(),
|
||||||
|
Colour = colours.ForHitResult(result),
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
|
||||||
|
Text = count.ToString(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ModCell : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly Mod mod;
|
||||||
|
|
||||||
|
public ModCell(Mod mod)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
this.mod = mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
FillFlowContainer container;
|
||||||
|
InternalChild = container = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Height = 15,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(2f, 0f),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new ModIcon(mod, showTooltip: false).With(icon =>
|
||||||
|
{
|
||||||
|
icon.Origin = Anchor.CentreLeft;
|
||||||
|
icon.Anchor = Anchor.CentreLeft;
|
||||||
|
icon.Scale = new Vector2(15f / icon.Height);
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
string description = mod.SettingDescription;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(description))
|
||||||
|
{
|
||||||
|
container.Add(new OsuSpriteText
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
|
||||||
|
Text = mod.SettingDescription,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Margin = new MarginPadding { Top = 1 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -115,6 +115,9 @@ namespace osu.Game.Online.Spectator
|
|||||||
|
|
||||||
Task ISpectatorClient.UserSentFrames(int userId, FrameDataBundle data)
|
Task ISpectatorClient.UserSentFrames(int userId, FrameDataBundle data)
|
||||||
{
|
{
|
||||||
|
if (data.Frames.Count > 0)
|
||||||
|
data.Frames[^1].Header = data.Header;
|
||||||
|
|
||||||
Schedule(() => OnNewFrames?.Invoke(userId, data));
|
Schedule(() => OnNewFrames?.Invoke(userId, data));
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
@ -247,7 +247,7 @@ namespace osu.Game
|
|||||||
var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
|
var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
|
||||||
|
|
||||||
// ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
|
// ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
|
||||||
dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, realm, Scheduler, Host, () => difficultyCache, LocalConfig));
|
dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, realm, Scheduler, () => difficultyCache, LocalConfig));
|
||||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, realm, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, performOnlineLookups: true));
|
dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, realm, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, performOnlineLookups: true));
|
||||||
|
|
||||||
dependencies.Cache(BeatmapDownloader = new BeatmapModelDownloader(BeatmapManager, API));
|
dependencies.Cache(BeatmapDownloader = new BeatmapModelDownloader(BeatmapManager, API));
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Input.StateChanges;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
|
|
||||||
@ -174,5 +175,19 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
|
|
||||||
return Frames[index].Time;
|
return Frames[index].Time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed override void CollectPendingInputs(List<IInput> inputs)
|
||||||
|
{
|
||||||
|
base.CollectPendingInputs(inputs);
|
||||||
|
|
||||||
|
CollectReplayInputs(inputs);
|
||||||
|
|
||||||
|
if (CurrentFrame?.Header != null)
|
||||||
|
inputs.Add(new ReplayStatisticsFrameInput { Frame = CurrentFrame });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void CollectReplayInputs(List<IInput> inputs)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,29 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using MessagePack;
|
using MessagePack;
|
||||||
|
using osu.Game.Online.Spectator;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Replays
|
namespace osu.Game.Rulesets.Replays
|
||||||
{
|
{
|
||||||
[MessagePackObject]
|
[MessagePackObject]
|
||||||
public class ReplayFrame
|
public class ReplayFrame
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The time at which this <see cref="ReplayFrame"/> takes place.
|
||||||
|
/// </summary>
|
||||||
[Key(0)]
|
[Key(0)]
|
||||||
public double Time;
|
public double Time;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="FrameHeader"/> containing the state of a play after this <see cref="ReplayFrame"/> takes place.
|
||||||
|
/// May be omitted where exact per-frame accuracy is not required.
|
||||||
|
/// </summary>
|
||||||
|
[IgnoreMember]
|
||||||
|
public FrameHeader? Header;
|
||||||
|
|
||||||
public ReplayFrame()
|
public ReplayFrame()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -149,6 +149,10 @@ namespace osu.Game.Rulesets
|
|||||||
var instanceInfo = (Activator.CreateInstance(resolvedType) as Ruleset)?.RulesetInfo
|
var instanceInfo = (Activator.CreateInstance(resolvedType) as Ruleset)?.RulesetInfo
|
||||||
?? throw new RulesetLoadException(@"Instantiation failure");
|
?? throw new RulesetLoadException(@"Instantiation failure");
|
||||||
|
|
||||||
|
// If a ruleset isn't up-to-date with the API, it could cause a crash at an arbitrary point of execution.
|
||||||
|
// To eagerly handle cases of missing implementations, enumerate all types here and mark as non-available on throw.
|
||||||
|
resolvedType.Assembly.GetTypes();
|
||||||
|
|
||||||
r.Name = instanceInfo.Name;
|
r.Name = instanceInfo.Name;
|
||||||
r.ShortName = instanceInfo.ShortName;
|
r.ShortName = instanceInfo.ShortName;
|
||||||
r.InstantiationInfo = instanceInfo.InstantiationInfo;
|
r.InstantiationInfo = instanceInfo.InstantiationInfo;
|
||||||
|
@ -176,7 +176,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// An array of all scorable <see cref="HitResult"/>s.
|
/// An array of all scorable <see cref="HitResult"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly HitResult[] SCORABLE_TYPES = ((HitResult[])Enum.GetValues(typeof(HitResult))).Where(r => r.IsScorable()).ToArray();
|
public static readonly HitResult[] ALL_TYPES = ((HitResult[])Enum.GetValues(typeof(HitResult))).ToArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a <see cref="HitResult"/> is valid within a given <see cref="HitResult"/> range.
|
/// Whether a <see cref="HitResult"/> is valid within a given <see cref="HitResult"/> range.
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Scoring
|
namespace osu.Game.Rulesets.Scoring
|
||||||
{
|
{
|
||||||
@ -107,6 +108,25 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
JudgedHits = 0;
|
JudgedHits = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reset all statistics based on header information contained within a replay frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If the provided replay frame does not have any header information, this will be a noop.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="ruleset">The ruleset to be used for retrieving statistics.</param>
|
||||||
|
/// <param name="frame">The replay frame to read header statistics from.</param>
|
||||||
|
public virtual void ResetFromReplayFrame(Ruleset ruleset, ReplayFrame frame)
|
||||||
|
{
|
||||||
|
if (frame.Header == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
JudgedHits = 0;
|
||||||
|
|
||||||
|
foreach ((_, int count) in frame.Header.Statistics)
|
||||||
|
JudgedHits += count;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>.
|
/// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -7,9 +7,11 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Scoring
|
namespace osu.Game.Rulesets.Scoring
|
||||||
@ -18,6 +20,11 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
{
|
{
|
||||||
private const double max_score = 1000000;
|
private const double max_score = 1000000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when this <see cref="ScoreProcessor"/> was reset from a replay frame.
|
||||||
|
/// </summary>
|
||||||
|
public event Action OnResetFromReplayFrame;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current total score.
|
/// The current total score.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -125,6 +132,8 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
if (result.FailedAtJudgement)
|
if (result.FailedAtJudgement)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) + 1;
|
||||||
|
|
||||||
if (!result.Type.IsScorable())
|
if (!result.Type.IsScorable())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -151,8 +160,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
rollingMaxBaseScore += result.Judgement.MaxNumericResult;
|
rollingMaxBaseScore += result.Judgement.MaxNumericResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) + 1;
|
|
||||||
|
|
||||||
hitEvents.Add(CreateHitEvent(result));
|
hitEvents.Add(CreateHitEvent(result));
|
||||||
lastHitObject = result.HitObject;
|
lastHitObject = result.HitObject;
|
||||||
|
|
||||||
@ -175,6 +182,8 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
if (result.FailedAtJudgement)
|
if (result.FailedAtJudgement)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) - 1;
|
||||||
|
|
||||||
if (!result.Type.IsScorable())
|
if (!result.Type.IsScorable())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -186,8 +195,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
|
rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) - 1;
|
|
||||||
|
|
||||||
Debug.Assert(hitEvents.Count > 0);
|
Debug.Assert(hitEvents.Count > 0);
|
||||||
lastHitObject = hitEvents[^1].LastHitObject;
|
lastHitObject = hitEvents[^1].LastHitObject;
|
||||||
hitEvents.RemoveAt(hitEvents.Count - 1);
|
hitEvents.RemoveAt(hitEvents.Count - 1);
|
||||||
@ -329,12 +336,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
HighestCombo.Value = 0;
|
HighestCombo.Value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
base.Dispose(isDisposing);
|
|
||||||
hitEvents.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a score populated with data for the current play this processor is responsible for.
|
/// Retrieve a score populated with data for the current play this processor is responsible for.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -346,11 +347,72 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
score.Accuracy = Accuracy.Value;
|
score.Accuracy = Accuracy.Value;
|
||||||
score.Rank = Rank.Value;
|
score.Rank = Rank.Value;
|
||||||
|
|
||||||
foreach (var result in HitResultExtensions.SCORABLE_TYPES)
|
foreach (var result in HitResultExtensions.ALL_TYPES)
|
||||||
score.Statistics[result] = GetStatistic(result);
|
score.Statistics[result] = GetStatistic(result);
|
||||||
|
|
||||||
score.HitEvents = hitEvents;
|
score.HitEvents = hitEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum <see cref="HitResult"/> for a normal hit (i.e. not tick/bonus) for this ruleset. Only populated via <see cref="ResetFromReplayFrame"/>.
|
||||||
|
/// </summary>
|
||||||
|
private HitResult? maxNormalResult;
|
||||||
|
|
||||||
|
public override void ResetFromReplayFrame(Ruleset ruleset, ReplayFrame frame)
|
||||||
|
{
|
||||||
|
base.ResetFromReplayFrame(ruleset, frame);
|
||||||
|
|
||||||
|
if (frame.Header == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
baseScore = 0;
|
||||||
|
rollingMaxBaseScore = 0;
|
||||||
|
HighestCombo.Value = frame.Header.MaxCombo;
|
||||||
|
|
||||||
|
foreach ((HitResult result, int count) in frame.Header.Statistics)
|
||||||
|
{
|
||||||
|
// Bonus scores are counted separately directly from the statistics dictionary later on.
|
||||||
|
if (!result.IsScorable() || result.IsBonus())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// The maximum result of this judgement if it wasn't a miss.
|
||||||
|
// E.g. For a GOOD judgement, the max result is either GREAT/PERFECT depending on which one the ruleset uses (osu!: GREAT, osu!mania: PERFECT).
|
||||||
|
HitResult maxResult;
|
||||||
|
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case HitResult.LargeTickHit:
|
||||||
|
case HitResult.LargeTickMiss:
|
||||||
|
maxResult = HitResult.LargeTickHit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HitResult.SmallTickHit:
|
||||||
|
case HitResult.SmallTickMiss:
|
||||||
|
maxResult = HitResult.SmallTickHit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
maxResult = maxNormalResult ??= ruleset.GetHitResults().OrderByDescending(kvp => Judgement.ToNumericResult(kvp.result)).First().result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
baseScore += count * Judgement.ToNumericResult(result);
|
||||||
|
rollingMaxBaseScore += count * Judgement.ToNumericResult(maxResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
scoreResultCounts.Clear();
|
||||||
|
scoreResultCounts.AddRange(frame.Header.Statistics);
|
||||||
|
|
||||||
|
updateScore();
|
||||||
|
|
||||||
|
OnResetFromReplayFrame?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
hitEvents.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ScoringMode
|
public enum ScoringMode
|
||||||
|
@ -16,6 +16,7 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Input;
|
using osu.Game.Input;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
||||||
|
|
||||||
@ -26,6 +27,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
public readonly KeyBindingContainer<T> KeyBindingContainer;
|
public readonly KeyBindingContainer<T> KeyBindingContainer;
|
||||||
|
|
||||||
|
private readonly Ruleset ruleset;
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private ScoreProcessor scoreProcessor { get; set; }
|
||||||
|
|
||||||
private ReplayRecorder recorder;
|
private ReplayRecorder recorder;
|
||||||
|
|
||||||
public ReplayRecorder Recorder
|
public ReplayRecorder Recorder
|
||||||
@ -51,6 +57,8 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||||
{
|
{
|
||||||
|
this.ruleset = ruleset.CreateInstance();
|
||||||
|
|
||||||
InternalChild = KeyBindingContainer =
|
InternalChild = KeyBindingContainer =
|
||||||
CreateKeyBindingContainer(ruleset, variant, unique)
|
CreateKeyBindingContainer(ruleset, variant, unique)
|
||||||
.WithChild(content = new Container { RelativeSizeAxes = Axes.Both });
|
.WithChild(content = new Container { RelativeSizeAxes = Axes.Both });
|
||||||
@ -66,17 +74,23 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public override void HandleInputStateChange(InputStateChangeEvent inputStateChange)
|
public override void HandleInputStateChange(InputStateChangeEvent inputStateChange)
|
||||||
{
|
{
|
||||||
if (inputStateChange is ReplayStateChangeEvent<T> replayStateChanged)
|
switch (inputStateChange)
|
||||||
{
|
{
|
||||||
foreach (var action in replayStateChanged.ReleasedActions)
|
case ReplayStateChangeEvent<T> stateChangeEvent:
|
||||||
KeyBindingContainer.TriggerReleased(action);
|
foreach (var action in stateChangeEvent.ReleasedActions)
|
||||||
|
KeyBindingContainer.TriggerReleased(action);
|
||||||
|
|
||||||
foreach (var action in replayStateChanged.PressedActions)
|
foreach (var action in stateChangeEvent.PressedActions)
|
||||||
KeyBindingContainer.TriggerPressed(action);
|
KeyBindingContainer.TriggerPressed(action);
|
||||||
}
|
break;
|
||||||
else
|
|
||||||
{
|
case ReplayStatisticsFrameEvent statisticsStateChangeEvent:
|
||||||
base.HandleInputStateChange(inputStateChange);
|
scoreProcessor?.ResetFromReplayFrame(ruleset, statisticsStateChangeEvent.Frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
base.HandleInputStateChange(inputStateChange);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Scoring
|
|||||||
private readonly ScoreModelManager scoreModelManager;
|
private readonly ScoreModelManager scoreModelManager;
|
||||||
|
|
||||||
public ScoreManager(RulesetStore rulesets, Func<BeatmapManager> beatmaps, Storage storage, RealmAccess realm, Scheduler scheduler,
|
public ScoreManager(RulesetStore rulesets, Func<BeatmapManager> beatmaps, Storage storage, RealmAccess realm, Scheduler scheduler,
|
||||||
IIpcHost importHost = null, Func<BeatmapDifficultyCache> difficulties = null, OsuConfigManager configManager = null)
|
Func<BeatmapDifficultyCache> difficulties = null, OsuConfigManager configManager = null)
|
||||||
{
|
{
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
}
|
}
|
||||||
|
|
||||||
Columns = createHeaders();
|
Columns = createHeaders();
|
||||||
Content = value.Select((g, i) => createContent(i, g)).ToArray().ToRectangular();
|
Content = value.Select(createContent).ToArray().ToRectangular();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
return columns.ToArray();
|
return columns.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable[] createContent(int index, ControlPointGroup group)
|
private Drawable[] createContent(ControlPointGroup group)
|
||||||
{
|
{
|
||||||
return new Drawable[]
|
return new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
|||||||
{
|
{
|
||||||
private readonly APIUserScoreAggregate score;
|
private readonly APIUserScoreAggregate score;
|
||||||
|
|
||||||
|
public override ScoreInfo TooltipContent => null; // match aggregate scores can't show statistics that the custom tooltip displays.
|
||||||
|
|
||||||
public MatchLeaderboardScore(APIUserScoreAggregate score, int? rank, bool isOnlineScope = true)
|
public MatchLeaderboardScore(APIUserScoreAggregate score, int? rank, bool isOnlineScope = true)
|
||||||
: base(score.CreateScoreInfo(), rank, isOnlineScope)
|
: base(score.CreateScoreInfo(), rank, isOnlineScope)
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Colour;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -49,7 +48,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChild = new FillFlowContainer
|
InternalChild = new FillFlowContainer
|
||||||
{
|
{
|
||||||
@ -127,7 +126,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
createColourBars(colours);
|
createColourBars();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -150,7 +149,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
iconLate.Rotation = -Rotation;
|
iconLate.Rotation = -Rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createColourBars(OsuColour colours)
|
private void createColourBars()
|
||||||
{
|
{
|
||||||
var windows = HitWindows.GetAllAvailableWindows().ToArray();
|
var windows = HitWindows.GetAllAvailableWindows().ToArray();
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (isRolling)
|
if (isRolling)
|
||||||
onDisplayedCountRolling(displayedCount, value);
|
onDisplayedCountRolling(value);
|
||||||
else if (displayedCount + 1 == value)
|
else if (displayedCount + 1 == value)
|
||||||
onDisplayedCountIncrement(value);
|
onDisplayedCountIncrement(value);
|
||||||
else
|
else
|
||||||
@ -151,7 +151,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
if (prev + 1 == Current.Value)
|
if (prev + 1 == Current.Value)
|
||||||
onCountIncrement(prev, Current.Value);
|
onCountIncrement(prev, Current.Value);
|
||||||
else
|
else
|
||||||
onCountChange(prev, Current.Value);
|
onCountChange(Current.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -226,7 +226,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
transformRoll(currentValue, newValue);
|
transformRoll(currentValue, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCountChange(int currentValue, int newValue)
|
private void onCountChange(int newValue)
|
||||||
{
|
{
|
||||||
scheduledPopOutCurrentId++;
|
scheduledPopOutCurrentId++;
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
DisplayedCount = newValue;
|
DisplayedCount = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDisplayedCountRolling(int currentValue, int newValue)
|
private void onDisplayedCountRolling(int newValue)
|
||||||
{
|
{
|
||||||
if (newValue == 0)
|
if (newValue == 0)
|
||||||
displayedCountSpriteText.FadeOut(fade_out_duration);
|
displayedCountSpriteText.FadeOut(fade_out_duration);
|
||||||
|
@ -163,6 +163,7 @@ namespace osu.Game.Screens.Play
|
|||||||
PrepareReplay();
|
PrepareReplay();
|
||||||
|
|
||||||
ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo);
|
ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo);
|
||||||
|
ScoreProcessor.OnResetFromReplayFrame += () => ScoreProcessor.PopulateScore(Score.ScoreInfo);
|
||||||
|
|
||||||
gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
||||||
}
|
}
|
||||||
@ -243,7 +244,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
// underlay and gameplay should have access to the skinning sources.
|
// underlay and gameplay should have access to the skinning sources.
|
||||||
createUnderlayComponents(),
|
createUnderlayComponents(),
|
||||||
createGameplayComponents(Beatmap.Value, playableBeatmap)
|
createGameplayComponents(Beatmap.Value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FailOverlay = new FailOverlay
|
FailOverlay = new FailOverlay
|
||||||
@ -356,7 +357,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private Drawable createUnderlayComponents() =>
|
private Drawable createUnderlayComponents() =>
|
||||||
DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both };
|
DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
private Drawable createGameplayComponents(IWorkingBeatmap working, IBeatmap playableBeatmap) => new ScalingContainer(ScalingMode.Gameplay)
|
private Drawable createGameplayComponents(IWorkingBeatmap working) => new ScalingContainer(ScalingMode.Gameplay)
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -72,6 +72,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
var convertedFrame = (ReplayFrame)convertibleFrame;
|
var convertedFrame = (ReplayFrame)convertibleFrame;
|
||||||
convertedFrame.Time = frame.Time;
|
convertedFrame.Time = frame.Time;
|
||||||
|
convertedFrame.Header = frame.Header;
|
||||||
|
|
||||||
score.Replay.Frames.Add(convertedFrame);
|
score.Replay.Frames.Add(convertedFrame);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
userFiles = new StorageBackedResourceStore(storage.GetStorageForDirectory("files"));
|
userFiles = new StorageBackedResourceStore(storage.GetStorageForDirectory("files"));
|
||||||
|
|
||||||
skinModelManager = new SkinModelManager(storage, realm, host, this);
|
skinModelManager = new SkinModelManager(storage, realm, this);
|
||||||
|
|
||||||
var defaultSkins = new[]
|
var defaultSkins = new[]
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
private readonly IStorageResourceProvider skinResources;
|
private readonly IStorageResourceProvider skinResources;
|
||||||
|
|
||||||
public SkinModelManager(Storage storage, RealmAccess realm, GameHost host, IStorageResourceProvider skinResources)
|
public SkinModelManager(Storage storage, RealmAccess realm, IStorageResourceProvider skinResources)
|
||||||
: base(storage, realm)
|
: base(storage, realm)
|
||||||
{
|
{
|
||||||
this.skinResources = skinResources;
|
this.skinResources = skinResources;
|
||||||
|
@ -126,7 +126,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue)
|
protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue)
|
||||||
{
|
{
|
||||||
return new TestBeatmapModelManager(storage, realm, rulesets, onlineLookupQueue);
|
return new TestBeatmapModelManager(storage, realm, onlineLookupQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore<byte[]> resources, IResourceStore<byte[]> storage, WorkingBeatmap defaultBeatmap, GameHost host)
|
protected override WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore<byte[]> resources, IResourceStore<byte[]> storage, WorkingBeatmap defaultBeatmap, GameHost host)
|
||||||
@ -150,7 +150,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
internal class TestBeatmapModelManager : BeatmapModelManager
|
internal class TestBeatmapModelManager : BeatmapModelManager
|
||||||
{
|
{
|
||||||
public TestBeatmapModelManager(Storage storage, RealmAccess databaseAccess, RulesetStore rulesetStore, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue)
|
public TestBeatmapModelManager(Storage storage, RealmAccess databaseAccess, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue)
|
||||||
: base(databaseAccess, storage, beatmapOnlineLookupQueue)
|
: base(databaseAccess, storage, beatmapOnlineLookupQueue)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.8.0" />
|
<PackageReference Include="Realm" Version="10.8.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.128.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.128.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.115.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.202.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.13.0" />
|
<PackageReference Include="Sentry" Version="3.13.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.128.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.128.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.115.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.202.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
Loading…
Reference in New Issue
Block a user