mirror of
https://github.com/ppy/osu.git
synced 2025-01-26 20:23:00 +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();
|
||||
|
||||
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);
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Pippidon.Replays
|
||||
|
||||
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);
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Replays
|
||||
|
||||
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>
|
||||
{
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Pippidon.Replays
|
||||
|
||||
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>
|
||||
{
|
||||
|
@ -51,7 +51,7 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
|
||||
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);
|
||||
|
||||
|
@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
|
||||
case CatchSkinComponents.CatchComboCounter:
|
||||
if (providesComboCounter)
|
||||
return new LegacyCatchComboCounter(Skin);
|
||||
return new LegacyCatchComboCounter();
|
||||
|
||||
return null;
|
||||
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
|
||||
private readonly LegacyRollingCounter explosion;
|
||||
|
||||
public LegacyCatchComboCounter(ISkin skin)
|
||||
public LegacyCatchComboCounter()
|
||||
{
|
||||
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,
|
||||
Width = 0.5f,
|
||||
Child = new ColumnHitObjectArea(0, new HitObjectContainer())
|
||||
Child = new ColumnHitObjectArea(new HitObjectContainer())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new ColumnHitObjectArea(1, new HitObjectContainer())
|
||||
Child = new ColumnHitObjectArea(new HitObjectContainer())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
|
@ -243,7 +243,8 @@ namespace osu.Game.Rulesets.Mania
|
||||
new ManiaModDifficultyAdjust(),
|
||||
new ManiaModClassic(),
|
||||
new ManiaModInvert(),
|
||||
new ManiaModConstantSpeed()
|
||||
new ManiaModConstantSpeed(),
|
||||
new ManiaModHoldOff()
|
||||
};
|
||||
|
||||
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 Type[] IncompatibleMods => new[] { typeof(ManiaModHoldOff) };
|
||||
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
|
||||
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>() });
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
sampleTriggerSource = new GameplaySampleTriggerSource(HitObjectContainer),
|
||||
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
|
||||
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())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
|
||||
private readonly Drawable hitTarget;
|
||||
|
||||
public ColumnHitObjectArea(int columnIndex, HitObjectContainer hitObjectContainer)
|
||||
public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
|
||||
: base(hitObjectContainer)
|
||||
{
|
||||
AddRangeInternal(new[]
|
||||
|
@ -118,7 +118,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => null;
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => null;
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
|
||||
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>() });
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Editing.Checks
|
||||
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.
|
||||
Assert.IsEmpty(check.Run(getContext(null, allowMissing: true)));
|
||||
Assert.IsEmpty(check.Run(getContext(null)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -91,7 +91,7 @@ namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
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);
|
||||
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 ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
switch (lookup)
|
||||
|
@ -1,11 +1,17 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
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.Tests.Visual;
|
||||
|
||||
@ -42,6 +48,43 @@ namespace osu.Game.Tests.Gameplay
|
||||
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
|
||||
{
|
||||
public override HitResult MaxResult { get; }
|
||||
|
@ -61,7 +61,6 @@ namespace osu.Game.Tests.NonVisual.Skinning
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => 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
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Online
|
||||
{
|
||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
||||
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]
|
||||
@ -173,14 +173,14 @@ namespace osu.Game.Tests.Online
|
||||
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
this.testBeatmapManager = testBeatmapManager;
|
||||
@ -196,7 +196,7 @@ namespace osu.Game.Tests.Online
|
||||
|
||||
internal class TestBeatmapModelDownloader : BeatmapModelDownloader
|
||||
{
|
||||
public TestBeatmapModelDownloader(IModelImporter<BeatmapSetInfo> importer, IAPIProvider apiProvider, GameHost gameHost)
|
||||
public TestBeatmapModelDownloader(IModelImporter<BeatmapSetInfo> importer, IAPIProvider 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 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 IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class SecondarySource : ISkin
|
||||
@ -314,8 +312,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => 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))]
|
||||
|
@ -218,6 +218,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
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 =>
|
||||
(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 ReplayState<TestAction> { PressedActions = CurrentFrame?.Actions ?? new List<TestAction>() });
|
||||
|
@ -197,7 +197,24 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 1,
|
||||
MaxCombo = 244,
|
||||
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,
|
||||
BeatmapInfo = beatmapInfo,
|
||||
User = new APIUser
|
||||
@ -217,7 +234,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 1,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
BeatmapInfo = beatmapInfo,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
User = new APIUser
|
||||
@ -237,7 +254,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 1,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
BeatmapInfo = beatmapInfo,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
|
||||
@ -258,7 +275,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 1,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
BeatmapInfo = beatmapInfo,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
|
||||
@ -279,7 +296,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 1,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
BeatmapInfo = beatmapInfo,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
|
||||
@ -300,7 +317,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 0.9826,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
BeatmapInfo = beatmapInfo,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
|
||||
@ -321,7 +338,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 0.9654,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
BeatmapInfo = beatmapInfo,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
|
||||
@ -342,7 +359,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 0.6025,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
BeatmapInfo = beatmapInfo,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
|
||||
@ -363,7 +380,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 0.5140,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
BeatmapInfo = beatmapInfo,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
|
||||
@ -384,7 +401,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Accuracy = 0.4222,
|
||||
MaxCombo = 244,
|
||||
TotalScore = 1707827,
|
||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||
BeatmapInfo = beatmapInfo,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
|
||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public BeatmapMetadata(RealmUser? user = null)
|
||||
{
|
||||
Author = new RealmUser();
|
||||
Author = user ?? new RealmUser();
|
||||
}
|
||||
|
||||
[UsedImplicitly] // Realm
|
||||
|
@ -30,6 +30,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
Alpha = 0.9f,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
new FillFlowContainer
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Database
|
||||
return;
|
||||
}
|
||||
|
||||
perform(retrieveFromID(r, ID));
|
||||
perform(retrieveFromID(r));
|
||||
RealmLiveStatistics.USAGE_ASYNC.Value++;
|
||||
});
|
||||
}
|
||||
@ -85,7 +85,7 @@ namespace osu.Game.Database
|
||||
|
||||
return realm.Run(r =>
|
||||
{
|
||||
var returnData = perform(retrieveFromID(r, ID));
|
||||
var returnData = perform(retrieveFromID(r));
|
||||
RealmLiveStatistics.USAGE_ASYNC.Value++;
|
||||
|
||||
if (returnData is RealmObjectBase realmObject && realmObject.IsManaged)
|
||||
@ -139,11 +139,11 @@ namespace osu.Game.Database
|
||||
}
|
||||
|
||||
dataIsFromUpdateThread = true;
|
||||
data = retrieveFromID(realm.Realm, ID);
|
||||
data = retrieveFromID(realm.Realm);
|
||||
RealmLiveStatistics.USAGE_UPDATE_REFETCH.Value++;
|
||||
}
|
||||
|
||||
private T retrieveFromID(Realm realm, Guid id)
|
||||
private T retrieveFromID(Realm realm)
|
||||
{
|
||||
var found = realm.Find<T>(ID);
|
||||
|
||||
|
@ -10,6 +10,8 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Configuration;
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Events;
|
||||
@ -30,13 +32,17 @@ namespace osu.Game.Graphics.Cursor
|
||||
private DragRotationState dragRotationState;
|
||||
private Vector2 positionMouseDown;
|
||||
|
||||
private Sample tapSample;
|
||||
|
||||
[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);
|
||||
|
||||
if (screenshotManager != null)
|
||||
screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility);
|
||||
|
||||
tapSample = audio.Samples.Get(@"UI/cursor-tap");
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
@ -87,6 +93,8 @@ namespace osu.Game.Graphics.Cursor
|
||||
dragRotationState = DragRotationState.DragStarted;
|
||||
positionMouseDown = e.MousePosition;
|
||||
}
|
||||
|
||||
playTapSample();
|
||||
}
|
||||
|
||||
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);
|
||||
dragRotationState = DragRotationState.NotDragging;
|
||||
}
|
||||
|
||||
if (State.Value == Visibility.Visible)
|
||||
playTapSample(0.8);
|
||||
}
|
||||
|
||||
base.OnMouseUp(e);
|
||||
@ -121,6 +132,18 @@ namespace osu.Game.Graphics.Cursor
|
||||
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
|
||||
{
|
||||
private Container cursorContainer;
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Input.StateChanges;
|
||||
using osu.Framework.Input.StateChanges.Events;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
|
||||
@ -79,5 +80,38 @@ namespace osu.Game.Input.Handlers
|
||||
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
|
||||
{
|
||||
public class LeaderboardScore : OsuClickableContainer, IHasContextMenu
|
||||
public class LeaderboardScore : OsuClickableContainer, IHasContextMenu, IHasCustomTooltip<ScoreInfo>
|
||||
{
|
||||
public const float HEIGHT = 60;
|
||||
|
||||
@ -70,6 +70,9 @@ namespace osu.Game.Online.Leaderboards
|
||||
[Resolved]
|
||||
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)
|
||||
{
|
||||
Score = score;
|
||||
@ -183,7 +186,6 @@ namespace osu.Game.Online.Leaderboards
|
||||
Anchor = Anchor.BottomLeft,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10f, 0f),
|
||||
Margin = new MarginPadding { Left = edge_margin },
|
||||
Children = statisticsLabels
|
||||
},
|
||||
@ -228,7 +230,6 @@ namespace osu.Game.Online.Leaderboards
|
||||
Origin = Anchor.BottomRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(1),
|
||||
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,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding { Right = 10 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (data.Frames.Count > 0)
|
||||
data.Frames[^1].Header = data.Header;
|
||||
|
||||
Schedule(() => OnNewFrames?.Invoke(userId, data));
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
@ -247,7 +247,7 @@ namespace osu.Game
|
||||
var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures);
|
||||
|
||||
// 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(BeatmapDownloader = new BeatmapModelDownloader(BeatmapManager, API));
|
||||
|
@ -6,6 +6,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Replays;
|
||||
|
||||
@ -174,5 +175,19 @@ namespace osu.Game.Rulesets.Replays
|
||||
|
||||
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.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using MessagePack;
|
||||
using osu.Game.Online.Spectator;
|
||||
|
||||
namespace osu.Game.Rulesets.Replays
|
||||
{
|
||||
[MessagePackObject]
|
||||
public class ReplayFrame
|
||||
{
|
||||
/// <summary>
|
||||
/// The time at which this <see cref="ReplayFrame"/> takes place.
|
||||
/// </summary>
|
||||
[Key(0)]
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
@ -149,6 +149,10 @@ namespace osu.Game.Rulesets
|
||||
var instanceInfo = (Activator.CreateInstance(resolvedType) as Ruleset)?.RulesetInfo
|
||||
?? 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.ShortName = instanceInfo.ShortName;
|
||||
r.InstantiationInfo = instanceInfo.InstantiationInfo;
|
||||
|
@ -176,7 +176,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// <summary>
|
||||
/// An array of all scorable <see cref="HitResult"/>s.
|
||||
/// </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>
|
||||
/// 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.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
@ -107,6 +108,25 @@ namespace osu.Game.Rulesets.Scoring
|
||||
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>
|
||||
/// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>.
|
||||
/// </summary>
|
||||
|
@ -7,9 +7,11 @@ using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Scoring
|
||||
@ -18,6 +20,11 @@ namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
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>
|
||||
/// The current total score.
|
||||
/// </summary>
|
||||
@ -125,6 +132,8 @@ namespace osu.Game.Rulesets.Scoring
|
||||
if (result.FailedAtJudgement)
|
||||
return;
|
||||
|
||||
scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) + 1;
|
||||
|
||||
if (!result.Type.IsScorable())
|
||||
return;
|
||||
|
||||
@ -151,8 +160,6 @@ namespace osu.Game.Rulesets.Scoring
|
||||
rollingMaxBaseScore += result.Judgement.MaxNumericResult;
|
||||
}
|
||||
|
||||
scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) + 1;
|
||||
|
||||
hitEvents.Add(CreateHitEvent(result));
|
||||
lastHitObject = result.HitObject;
|
||||
|
||||
@ -175,6 +182,8 @@ namespace osu.Game.Rulesets.Scoring
|
||||
if (result.FailedAtJudgement)
|
||||
return;
|
||||
|
||||
scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) - 1;
|
||||
|
||||
if (!result.Type.IsScorable())
|
||||
return;
|
||||
|
||||
@ -186,8 +195,6 @@ namespace osu.Game.Rulesets.Scoring
|
||||
rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
|
||||
}
|
||||
|
||||
scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) - 1;
|
||||
|
||||
Debug.Assert(hitEvents.Count > 0);
|
||||
lastHitObject = hitEvents[^1].LastHitObject;
|
||||
hitEvents.RemoveAt(hitEvents.Count - 1);
|
||||
@ -329,12 +336,6 @@ namespace osu.Game.Rulesets.Scoring
|
||||
HighestCombo.Value = 0;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
hitEvents.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a score populated with data for the current play this processor is responsible for.
|
||||
/// </summary>
|
||||
@ -346,11 +347,72 @@ namespace osu.Game.Rulesets.Scoring
|
||||
score.Accuracy = Accuracy.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.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
|
||||
|
@ -16,6 +16,7 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Input;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
||||
|
||||
@ -26,6 +27,11 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
public readonly KeyBindingContainer<T> KeyBindingContainer;
|
||||
|
||||
private readonly Ruleset ruleset;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private ScoreProcessor scoreProcessor { get; set; }
|
||||
|
||||
private ReplayRecorder recorder;
|
||||
|
||||
public ReplayRecorder Recorder
|
||||
@ -51,6 +57,8 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
{
|
||||
this.ruleset = ruleset.CreateInstance();
|
||||
|
||||
InternalChild = KeyBindingContainer =
|
||||
CreateKeyBindingContainer(ruleset, variant, unique)
|
||||
.WithChild(content = new Container { RelativeSizeAxes = Axes.Both });
|
||||
@ -66,17 +74,23 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public override void HandleInputStateChange(InputStateChangeEvent inputStateChange)
|
||||
{
|
||||
if (inputStateChange is ReplayStateChangeEvent<T> replayStateChanged)
|
||||
switch (inputStateChange)
|
||||
{
|
||||
foreach (var action in replayStateChanged.ReleasedActions)
|
||||
KeyBindingContainer.TriggerReleased(action);
|
||||
case ReplayStateChangeEvent<T> stateChangeEvent:
|
||||
foreach (var action in stateChangeEvent.ReleasedActions)
|
||||
KeyBindingContainer.TriggerReleased(action);
|
||||
|
||||
foreach (var action in replayStateChanged.PressedActions)
|
||||
KeyBindingContainer.TriggerPressed(action);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.HandleInputStateChange(inputStateChange);
|
||||
foreach (var action in stateChangeEvent.PressedActions)
|
||||
KeyBindingContainer.TriggerPressed(action);
|
||||
break;
|
||||
|
||||
case ReplayStatisticsFrameEvent statisticsStateChangeEvent:
|
||||
scoreProcessor?.ResetFromReplayFrame(ruleset, statisticsStateChangeEvent.Frame);
|
||||
break;
|
||||
|
||||
default:
|
||||
base.HandleInputStateChange(inputStateChange);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Scoring
|
||||
private readonly ScoreModelManager scoreModelManager;
|
||||
|
||||
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.scheduler = scheduler;
|
||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
private Drawable[] createContent(int index, ControlPointGroup group)
|
||||
private Drawable[] createContent(ControlPointGroup group)
|
||||
{
|
||||
return new Drawable[]
|
||||
{
|
||||
|
@ -14,6 +14,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
{
|
||||
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)
|
||||
: base(score.CreateScoreInfo(), rank, isOnlineScope)
|
||||
{
|
||||
|
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
@ -49,7 +48,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load()
|
||||
{
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
@ -127,7 +126,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
}
|
||||
};
|
||||
|
||||
createColourBars(colours);
|
||||
createColourBars();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -150,7 +149,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
||||
iconLate.Rotation = -Rotation;
|
||||
}
|
||||
|
||||
private void createColourBars(OsuColour colours)
|
||||
private void createColourBars()
|
||||
{
|
||||
var windows = HitWindows.GetAllAvailableWindows().ToArray();
|
||||
|
||||
|
@ -109,7 +109,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
return;
|
||||
|
||||
if (isRolling)
|
||||
onDisplayedCountRolling(displayedCount, value);
|
||||
onDisplayedCountRolling(value);
|
||||
else if (displayedCount + 1 == value)
|
||||
onDisplayedCountIncrement(value);
|
||||
else
|
||||
@ -151,7 +151,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
if (prev + 1 == Current.Value)
|
||||
onCountIncrement(prev, Current.Value);
|
||||
else
|
||||
onCountChange(prev, Current.Value);
|
||||
onCountChange(Current.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -226,7 +226,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
transformRoll(currentValue, newValue);
|
||||
}
|
||||
|
||||
private void onCountChange(int currentValue, int newValue)
|
||||
private void onCountChange(int newValue)
|
||||
{
|
||||
scheduledPopOutCurrentId++;
|
||||
|
||||
@ -236,7 +236,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
DisplayedCount = newValue;
|
||||
}
|
||||
|
||||
private void onDisplayedCountRolling(int currentValue, int newValue)
|
||||
private void onDisplayedCountRolling(int newValue)
|
||||
{
|
||||
if (newValue == 0)
|
||||
displayedCountSpriteText.FadeOut(fade_out_duration);
|
||||
|
@ -163,6 +163,7 @@ namespace osu.Game.Screens.Play
|
||||
PrepareReplay();
|
||||
|
||||
ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo);
|
||||
ScoreProcessor.OnResetFromReplayFrame += () => ScoreProcessor.PopulateScore(Score.ScoreInfo);
|
||||
|
||||
gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
||||
}
|
||||
@ -243,7 +244,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
// underlay and gameplay should have access to the skinning sources.
|
||||
createUnderlayComponents(),
|
||||
createGameplayComponents(Beatmap.Value, playableBeatmap)
|
||||
createGameplayComponents(Beatmap.Value)
|
||||
}
|
||||
},
|
||||
FailOverlay = new FailOverlay
|
||||
@ -356,7 +357,7 @@ namespace osu.Game.Screens.Play
|
||||
private Drawable createUnderlayComponents() =>
|
||||
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[]
|
||||
{
|
||||
|
@ -72,6 +72,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
var convertedFrame = (ReplayFrame)convertibleFrame;
|
||||
convertedFrame.Time = frame.Time;
|
||||
convertedFrame.Header = frame.Header;
|
||||
|
||||
score.Replay.Frames.Add(convertedFrame);
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
userFiles = new StorageBackedResourceStore(storage.GetStorageForDirectory("files"));
|
||||
|
||||
skinModelManager = new SkinModelManager(storage, realm, host, this);
|
||||
skinModelManager = new SkinModelManager(storage, realm, this);
|
||||
|
||||
var defaultSkins = new[]
|
||||
{
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
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)
|
||||
{
|
||||
this.skinResources = skinResources;
|
||||
|
@ -126,7 +126,7 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
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)
|
||||
@ -150,7 +150,7 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.8.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="SharpCompress" Version="0.30.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
|
@ -61,7 +61,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<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>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
<PropertyGroup>
|
||||
|
Loading…
Reference in New Issue
Block a user