mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:33:30 +08:00
Merge branch 'master' into availability-fixes
This commit is contained in:
commit
4d9c3091a1
@ -72,13 +72,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
seekTo(200);
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -100,13 +100,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
|
||||
seekTo(200);
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -145,23 +145,23 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is first hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(first));
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_NORMAL);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_NORMAL);
|
||||
|
||||
seekTo(120);
|
||||
AddAssert("most valid object is first hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(first));
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_NORMAL);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_NORMAL);
|
||||
|
||||
seekTo(480);
|
||||
AddAssert("most valid object is second hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(second));
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
|
||||
seekTo(700);
|
||||
AddAssert("most valid object is second hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(second));
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -174,8 +174,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
StartTime = 100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "drum"),
|
||||
new HitSampleInfo(HitSampleInfo.HIT_FINISH, "drum") // implies strong
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM),
|
||||
new HitSampleInfo(HitSampleInfo.HIT_FINISH, HitSampleInfo.BANK_DRUM) // implies strong
|
||||
}
|
||||
};
|
||||
hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
@ -184,13 +184,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is nested strong hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit.StrongNestedHit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
checkSamples(HitType.Centre, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
|
||||
seekTo(200);
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
checkSamples(HitType.Centre, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -213,18 +213,18 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
seekTo(600);
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
seekTo(1200);
|
||||
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -247,18 +247,18 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
|
||||
seekTo(600);
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
|
||||
seekTo(1200);
|
||||
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -272,8 +272,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
EndTime = 1100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "drum"),
|
||||
new HitSampleInfo(HitSampleInfo.HIT_FINISH, "drum") // implies strong
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM),
|
||||
new HitSampleInfo(HitSampleInfo.HIT_FINISH, HitSampleInfo.BANK_DRUM) // implies strong
|
||||
}
|
||||
};
|
||||
drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
@ -282,18 +282,18 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
checkSamples(HitType.Centre, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
|
||||
seekTo(600);
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
checkSamples(HitType.Centre, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
|
||||
seekTo(1200);
|
||||
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
checkSamples(HitType.Centre, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -319,18 +319,18 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
// This works fine in gameplay because they are judged whenever the user pressed, rather than being timed hits.
|
||||
// But for sample playback purposes they can be ignored as noise.
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
seekTo(600);
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
seekTo(1200);
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -344,7 +344,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
EndTime = 1100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "drum")
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM)
|
||||
}
|
||||
};
|
||||
swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
@ -356,25 +356,26 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
// This works fine in gameplay because they are judged whenever the user pressed, rather than being timed hits.
|
||||
// But for sample playback purposes they can be ignored as noise.
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
||||
|
||||
seekTo(600);
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
||||
|
||||
seekTo(1200);
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
||||
}
|
||||
|
||||
private void checkSound(HitType hitType, string expectedName, string expectedBank)
|
||||
private void checkSamples(HitType hitType, string expectedSamplesCsv, string expectedBank)
|
||||
{
|
||||
AddStep($"hit {hitType}", () => triggerSource.Play(hitType));
|
||||
AddAssert($"last played sample is {expectedName}", () => triggerSource.LastPlayedSamples!.OfType<HitSampleInfo>().Single().Name, () => Is.EqualTo(expectedName));
|
||||
AddAssert($"last played sample has {expectedBank} bank", () => triggerSource.LastPlayedSamples!.OfType<HitSampleInfo>().Single().Bank, () => Is.EqualTo(expectedBank));
|
||||
AddAssert($"last played sample is {expectedSamplesCsv}", () => string.Join(',', triggerSource.LastPlayedSamples!.OfType<HitSampleInfo>().Select(s => s.Name)),
|
||||
() => Is.EqualTo(expectedSamplesCsv));
|
||||
AddAssert($"last played sample has {expectedBank} bank", () => triggerSource.LastPlayedSamples!.OfType<HitSampleInfo>().First().Bank, () => Is.EqualTo(expectedBank));
|
||||
}
|
||||
|
||||
private void seekTo(double time) => AddStep($"seek to {time}", () => gameplayClock.Seek(time));
|
||||
|
@ -3,15 +3,16 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Taiko has some interesting rules for legacy mappings.
|
||||
/// Taiko doesn't output any samples. They are all handled externally by <see cref="DrumSamplePlayer"/>.
|
||||
/// </summary>
|
||||
[HeadlessTest]
|
||||
public partial class TestSceneSampleOutput : TestSceneTaikoPlayer
|
||||
@ -26,10 +27,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
HitSampleInfo.HIT_FINISH,
|
||||
HitSampleInfo.HIT_WHISTLE,
|
||||
HitSampleInfo.HIT_WHISTLE,
|
||||
HitSampleInfo.HIT_WHISTLE,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
string.Empty,
|
||||
};
|
||||
|
||||
var actualSampleNames = new List<string>();
|
||||
@ -46,7 +47,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
|
||||
AddUntilStep("all samples collected", () => actualSampleNames.Count == expectedSampleNames.Length);
|
||||
|
||||
AddAssert("samples are correct", () => actualSampleNames.SequenceEqual(expectedSampleNames));
|
||||
AddAssert("samples are correct", () => actualSampleNames, () => Is.EqualTo(expectedSampleNames));
|
||||
}
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TaikoBeatmapConversionTest().GetBeatmap("sample-to-type-conversions");
|
||||
|
@ -4,14 +4,12 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Skinning.Default;
|
||||
@ -93,40 +91,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
? new SkinnableDrawable(new TaikoSkinComponentLookup(TaikoSkinComponents.CentreHit), _ => new CentreHitCirclePiece(), confineMode: ConfineMode.ScaleToFit)
|
||||
: new SkinnableDrawable(new TaikoSkinComponentLookup(TaikoSkinComponents.RimHit), _ => new RimHitCirclePiece(), confineMode: ConfineMode.ScaleToFit);
|
||||
|
||||
public override IEnumerable<HitSampleInfo> GetSamples()
|
||||
{
|
||||
// normal and claps are always handled by the drum (see DrumSampleMapping).
|
||||
// in addition, whistles are excluded as they are an alternative rim marker.
|
||||
|
||||
var samples = HitObject.Samples.Where(s =>
|
||||
s.Name != HitSampleInfo.HIT_NORMAL
|
||||
&& s.Name != HitSampleInfo.HIT_CLAP
|
||||
&& s.Name != HitSampleInfo.HIT_WHISTLE);
|
||||
|
||||
if (HitObject.Type == HitType.Rim && HitObject.IsStrong)
|
||||
{
|
||||
// strong + rim always maps to whistle.
|
||||
// TODO: this should really be in the legacy decoder, but can't be because legacy encoding parity would be broken.
|
||||
// when we add a taiko editor, this is probably not going to play nice.
|
||||
|
||||
var corrected = samples.ToList();
|
||||
|
||||
for (int i = 0; i < corrected.Count; i++)
|
||||
{
|
||||
var s = corrected[i];
|
||||
|
||||
if (s.Name != HitSampleInfo.HIT_FINISH)
|
||||
continue;
|
||||
|
||||
corrected[i] = s.With(HitSampleInfo.HIT_WHISTLE);
|
||||
}
|
||||
|
||||
return corrected;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
Debug.Assert(HitObject.HitWindows != null);
|
||||
|
@ -119,8 +119,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
}
|
||||
|
||||
// Most osu!taiko hitsounds are managed by the drum (see DrumSampleTriggerSource).
|
||||
public override IEnumerable<HitSampleInfo> GetSamples() => Enumerable.Empty<HitSampleInfo>();
|
||||
// osu!taiko hitsounds are managed by the drum (see DrumSampleTriggerSource).
|
||||
public sealed override IEnumerable<HitSampleInfo> GetSamples() => Enumerable.Empty<HitSampleInfo>();
|
||||
}
|
||||
|
||||
public abstract partial class DrawableTaikoHitObject<TObject> : DrawableTaikoHitObject
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@ -18,12 +17,25 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
public void Play(HitType hitType)
|
||||
{
|
||||
var hitSample = GetMostValidObject()?.Samples?.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL);
|
||||
TaikoHitObject? hitObject = GetMostValidObject() as TaikoHitObject;
|
||||
|
||||
if (hitSample == null)
|
||||
if (hitObject == null)
|
||||
return;
|
||||
|
||||
PlaySamples(new ISampleInfo[] { new HitSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL, hitSample.Bank, volume: hitSample.Volume) });
|
||||
var baseSample = hitObject.CreateHitSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL);
|
||||
|
||||
if ((hitObject as TaikoStrongableHitObject)?.IsStrong == true || hitObject is StrongNestedHitObject)
|
||||
{
|
||||
PlaySamples(new ISampleInfo[]
|
||||
{
|
||||
baseSample,
|
||||
hitObject.CreateHitSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_WHISTLE : HitSampleInfo.HIT_FINISH)
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySamples(new ISampleInfo[] { baseSample });
|
||||
}
|
||||
}
|
||||
|
||||
public override void Play() => throw new InvalidOperationException(@"Use override with HitType parameter instead");
|
||||
|
@ -18,6 +18,7 @@ using osu.Game.Extensions;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Tests.Resources;
|
||||
using Realms;
|
||||
using SharpCompress.Archives;
|
||||
@ -416,6 +417,53 @@ namespace osu.Game.Tests.Database
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestImport_ThenModifyMapWithScore_ThenImport()
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
string? temp = TestResources.GetTestBeatmapForImport();
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||
|
||||
await createScoreForBeatmap(realm.Realm, imported.Beatmaps.First());
|
||||
|
||||
// imitate making local changes via editor
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
realm.Write(_ =>
|
||||
{
|
||||
BeatmapInfo beatmap = imported.Beatmaps.First();
|
||||
beatmap.Hash = "new_hash";
|
||||
beatmap.ResetOnlineInfo();
|
||||
});
|
||||
|
||||
// for now, making changes to a beatmap doesn't remove the backlink from the score to the beatmap.
|
||||
// the logic of ensuring that scores match the beatmap is upheld via comparing the hash in usages (see: https://github.com/ppy/osu/pull/22539).
|
||||
// TODO: revisit when fixing https://github.com/ppy/osu/issues/24069.
|
||||
Assert.That(imported.Beatmaps.First().Scores.Any());
|
||||
|
||||
var importedSecondTime = await importer.Import(new ImportTask(temp));
|
||||
|
||||
EnsureLoaded(realm.Realm);
|
||||
|
||||
// check the newly "imported" beatmap is not the original.
|
||||
Assert.NotNull(importedSecondTime);
|
||||
Debug.Assert(importedSecondTime != null);
|
||||
Assert.That(imported.ID != importedSecondTime.ID);
|
||||
|
||||
var importedFirstTimeBeatmap = imported.Beatmaps.First();
|
||||
var importedSecondTimeBeatmap = importedSecondTime.PerformRead(s => s.Beatmaps.First());
|
||||
|
||||
Assert.That(importedFirstTimeBeatmap.ID != importedSecondTimeBeatmap.ID);
|
||||
Assert.That(importedFirstTimeBeatmap.Hash != importedSecondTimeBeatmap.Hash);
|
||||
Assert.That(!importedFirstTimeBeatmap.Scores.Any());
|
||||
Assert.That(importedSecondTimeBeatmap.Scores.Count() == 1);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestImportThenImportWithChangedFile()
|
||||
{
|
||||
@ -1074,18 +1122,16 @@ namespace osu.Game.Tests.Database
|
||||
Assert.IsTrue(realm.All<BeatmapSetInfo>().First(_ => true).DeletePending);
|
||||
}
|
||||
|
||||
private static Task createScoreForBeatmap(Realm realm, BeatmapInfo beatmap)
|
||||
private static Task createScoreForBeatmap(Realm realm, BeatmapInfo beatmap) =>
|
||||
realm.WriteAsync(() =>
|
||||
{
|
||||
// TODO: reimplement when we have score support in realm.
|
||||
// return ImportScoreTest.LoadScoreIntoOsu(osu, new ScoreInfo
|
||||
// {
|
||||
// OnlineID = 2,
|
||||
// Beatmap = beatmap,
|
||||
// BeatmapInfoID = beatmap.ID
|
||||
// }, new ImportScoreTest.TestArchiveReader());
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
realm.Add(new ScoreInfo
|
||||
{
|
||||
OnlineID = 2,
|
||||
BeatmapInfo = beatmap,
|
||||
BeatmapHash = beatmap.Hash
|
||||
});
|
||||
});
|
||||
|
||||
private static void checkBeatmapSetCount(Realm realm, int expected, bool includeDeletePending = false)
|
||||
{
|
||||
|
@ -347,6 +347,73 @@ namespace osu.Game.Tests.Database
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDanglingScoreTransferred()
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var rulesets = new RealmRulesetStore(realm, storage);
|
||||
|
||||
using var __ = getBeatmapArchive(out string pathOriginal);
|
||||
using var _ = getBeatmapArchive(out string pathOnlineCopy);
|
||||
|
||||
var importBeforeUpdate = await importer.Import(new ImportTask(pathOriginal));
|
||||
|
||||
Assert.That(importBeforeUpdate, Is.Not.Null);
|
||||
Debug.Assert(importBeforeUpdate != null);
|
||||
|
||||
string scoreTargetBeatmapHash = string.Empty;
|
||||
|
||||
// set a score on the beatmap
|
||||
importBeforeUpdate.PerformWrite(s =>
|
||||
{
|
||||
var beatmapInfo = s.Beatmaps.First();
|
||||
|
||||
scoreTargetBeatmapHash = beatmapInfo.Hash;
|
||||
|
||||
s.Realm.Add(new ScoreInfo(beatmapInfo, s.Realm.All<RulesetInfo>().First(), new RealmUser()));
|
||||
});
|
||||
|
||||
// locally modify beatmap
|
||||
const string new_beatmap_hash = "new_hash";
|
||||
importBeforeUpdate.PerformWrite(s =>
|
||||
{
|
||||
var beatmapInfo = s.Beatmaps.First(b => b.Hash == scoreTargetBeatmapHash);
|
||||
|
||||
beatmapInfo.Hash = new_beatmap_hash;
|
||||
beatmapInfo.ResetOnlineInfo();
|
||||
});
|
||||
|
||||
realm.Run(r => r.Refresh());
|
||||
|
||||
// for now, making changes to a beatmap doesn't remove the backlink from the score to the beatmap.
|
||||
// the logic of ensuring that scores match the beatmap is upheld via comparing the hash in usages (https://github.com/ppy/osu/pull/22539).
|
||||
// TODO: revisit when fixing https://github.com/ppy/osu/issues/24069.
|
||||
checkCount<ScoreInfo>(realm, 1);
|
||||
|
||||
// reimport the original beatmap before local modifications
|
||||
var importAfterUpdate = await importer.ImportAsUpdate(new ProgressNotification(), new ImportTask(pathOnlineCopy), importBeforeUpdate.Value);
|
||||
|
||||
Assert.That(importAfterUpdate, Is.Not.Null);
|
||||
Debug.Assert(importAfterUpdate != null);
|
||||
|
||||
realm.Run(r => r.Refresh());
|
||||
|
||||
// both original and locally modified versions present
|
||||
checkCount<BeatmapInfo>(realm, count_beatmaps + 1);
|
||||
checkCount<BeatmapMetadata>(realm, count_beatmaps + 1);
|
||||
checkCount<BeatmapSetInfo>(realm, 2);
|
||||
|
||||
// score is preserved
|
||||
checkCount<ScoreInfo>(realm, 1);
|
||||
|
||||
// score is transferred to new beatmap
|
||||
Assert.That(importBeforeUpdate.Value.Beatmaps.First(b => b.Hash == new_beatmap_hash).Scores, Has.Count.EqualTo(0));
|
||||
Assert.That(importAfterUpdate.Value.Beatmaps.First(b => b.Hash == scoreTargetBeatmapHash).Scores, Has.Count.EqualTo(1));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreLostOnModification()
|
||||
{
|
||||
|
@ -133,6 +133,32 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("objects reverted to original position", () => addedObjects[0].Position == new Vector2(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGlobalFlipHotkeys()
|
||||
{
|
||||
HitCircle addedObject = null;
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.Add(addedObject = new HitCircle { StartTime = 100 }));
|
||||
|
||||
AddStep("select objects", () => EditorBeatmap.SelectedHitObjects.Add(addedObject));
|
||||
|
||||
AddStep("flip horizontally across playfield", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ControlLeft);
|
||||
InputManager.Key(Key.H);
|
||||
InputManager.ReleaseKey(Key.ControlLeft);
|
||||
});
|
||||
AddAssert("objects flipped horizontally", () => addedObject.Position == new Vector2(OsuPlayfield.BASE_SIZE.X, 0));
|
||||
|
||||
AddStep("flip vertically across playfield", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ControlLeft);
|
||||
InputManager.Key(Key.J);
|
||||
InputManager.ReleaseKey(Key.ControlLeft);
|
||||
});
|
||||
AddAssert("objects flipped vertically", () => addedObject.Position == OsuPlayfield.BASE_SIZE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasicSelect()
|
||||
{
|
||||
|
@ -170,7 +170,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
ManualClock clock = null;
|
||||
|
||||
var beatmap = new Beatmap();
|
||||
beatmap.HitObjects.Add(new TestHitObjectWithNested { Duration = 40 });
|
||||
beatmap.HitObjects.Add(new TestHitObjectWithNested
|
||||
{
|
||||
Duration = 40,
|
||||
NestedObjects = new HitObject[]
|
||||
{
|
||||
new PooledNestedHitObject { StartTime = 10 },
|
||||
new PooledNestedHitObject { StartTime = 20 },
|
||||
new PooledNestedHitObject { StartTime = 30 }
|
||||
}
|
||||
});
|
||||
|
||||
createTest(beatmap, 10, () => new FramedClock(clock = new ManualClock()));
|
||||
|
||||
@ -209,6 +218,49 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("object judged", () => playfield.JudgedObjects.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPooledObjectWithNonPooledNesteds()
|
||||
{
|
||||
ManualClock clock = null;
|
||||
TestHitObjectWithNested hitObjectWithNested;
|
||||
|
||||
var beatmap = new Beatmap();
|
||||
beatmap.HitObjects.Add(hitObjectWithNested = new TestHitObjectWithNested
|
||||
{
|
||||
Duration = 40,
|
||||
NestedObjects = new HitObject[]
|
||||
{
|
||||
new PooledNestedHitObject { StartTime = 10 },
|
||||
new NonPooledNestedHitObject { StartTime = 20 },
|
||||
new NonPooledNestedHitObject { StartTime = 30 }
|
||||
}
|
||||
});
|
||||
|
||||
createTest(beatmap, 10, () => new FramedClock(clock = new ManualClock()));
|
||||
|
||||
AddAssert("hitobject entry has all nesteds", () => playfield.HitObjectContainer.Entries.Single().NestedEntries, () => Has.Count.EqualTo(3));
|
||||
|
||||
AddStep("skip to middle of object", () => clock.CurrentTime = (hitObjectWithNested.StartTime + hitObjectWithNested.GetEndTime()) / 2);
|
||||
AddAssert("2 objects judged", () => playfield.JudgedObjects.Count, () => Is.EqualTo(2));
|
||||
AddAssert("entry not all judged", () => playfield.HitObjectContainer.Entries.Single().AllJudged, () => Is.False);
|
||||
|
||||
AddStep("skip to before end of object", () => clock.CurrentTime = hitObjectWithNested.GetEndTime() - 1);
|
||||
AddAssert("3 objects judged", () => playfield.JudgedObjects.Count, () => Is.EqualTo(3));
|
||||
AddAssert("entry not all judged", () => playfield.HitObjectContainer.Entries.Single().AllJudged, () => Is.False);
|
||||
|
||||
AddStep("removing object doesn't crash", () => playfield.Remove(hitObjectWithNested));
|
||||
AddStep("clear judged", () => playfield.JudgedObjects.Clear());
|
||||
|
||||
AddStep("add object back", () => playfield.Add(hitObjectWithNested));
|
||||
AddAssert("entry not all judged", () => playfield.HitObjectContainer.Entries.Single().AllJudged, () => Is.False);
|
||||
|
||||
AddStep("skip to long past object", () => clock.CurrentTime = 100_000);
|
||||
// the parent entry should still be linked to nested entries of pooled objects that are managed externally
|
||||
// but not contain synthetic entries that were created for the non-pooled objects.
|
||||
AddAssert("entry still has non-synthetic nested entries", () => playfield.HitObjectContainer.Entries.Single().NestedEntries, () => Has.Count.EqualTo(1));
|
||||
AddAssert("entry all judged", () => playfield.HitObjectContainer.Entries.Single().AllJudged, () => Is.True);
|
||||
}
|
||||
|
||||
private void createTest(IBeatmap beatmap, int poolSize, Func<IFrameBasedClock> createClock = null)
|
||||
{
|
||||
AddStep("create test", () =>
|
||||
@ -289,7 +341,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
RegisterPool<TestHitObject, DrawableTestHitObject>(poolSize);
|
||||
RegisterPool<TestKilledHitObject, DrawableTestKilledHitObject>(poolSize);
|
||||
RegisterPool<TestHitObjectWithNested, DrawableTestHitObjectWithNested>(poolSize);
|
||||
RegisterPool<NestedHitObject, DrawableNestedHitObject>(poolSize);
|
||||
RegisterPool<PooledNestedHitObject, DrawableNestedHitObject>(poolSize);
|
||||
}
|
||||
|
||||
protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject);
|
||||
@ -422,16 +474,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private class TestHitObjectWithNested : TestHitObject
|
||||
{
|
||||
public IEnumerable<HitObject> NestedObjects { get; init; } = Array.Empty<HitObject>();
|
||||
|
||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||
{
|
||||
base.CreateNestedHitObjects(cancellationToken);
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
AddNested(new NestedHitObject { StartTime = (float)Duration * (i + 1) / 4 });
|
||||
foreach (var ho in NestedObjects)
|
||||
AddNested(ho);
|
||||
}
|
||||
}
|
||||
|
||||
private class NestedHitObject : ConvertHitObject
|
||||
private class PooledNestedHitObject : ConvertHitObject
|
||||
{
|
||||
}
|
||||
|
||||
private class NonPooledNestedHitObject : ConvertHitObject
|
||||
{
|
||||
}
|
||||
|
||||
@ -482,6 +540,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
nestedContainer.Clear(false);
|
||||
}
|
||||
|
||||
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
|
||||
=> hitObject is NonPooledNestedHitObject nonPooled ? new DrawableNestedHitObject(nonPooled) : null;
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
base.CheckForResult(userTriggered, timeOffset);
|
||||
@ -490,25 +551,30 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
private partial class DrawableNestedHitObject : DrawableHitObject<NestedHitObject>
|
||||
private partial class DrawableNestedHitObject : DrawableHitObject
|
||||
{
|
||||
public DrawableNestedHitObject()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public DrawableNestedHitObject(NestedHitObject hitObject)
|
||||
public DrawableNestedHitObject(PooledNestedHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
|
||||
public DrawableNestedHitObject(NonPooledNestedHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
Size = new Vector2(15);
|
||||
Colour = Colour4.White;
|
||||
RelativePositionAxes = Axes.Both;
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Size = new Vector2(15);
|
||||
Colour = Colour4.White;
|
||||
RelativePositionAxes = Axes.Both;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
AddInternal(new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
@ -188,7 +188,7 @@ namespace osu.Game.Tournament.Components
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DiffPiece(stats),
|
||||
new DiffPiece(("Star Rating", $"{beatmap.StarRating:0.##}{srExtra}"))
|
||||
new DiffPiece(("Star Rating", $"{beatmap.StarRating:0.00}{srExtra}"))
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
|
@ -20,6 +20,7 @@ using osu.Game.IO;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Scoring;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
@ -204,6 +205,15 @@ namespace osu.Game.Beatmaps
|
||||
protected override void PostImport(BeatmapSetInfo model, Realm realm, ImportParameters parameters)
|
||||
{
|
||||
base.PostImport(model, realm, parameters);
|
||||
|
||||
// Scores are stored separately from beatmaps, and persist even when a beatmap is modified or deleted.
|
||||
// Let's reattach any matching scores that exist in the database, based on hash.
|
||||
foreach (BeatmapInfo beatmap in model.Beatmaps)
|
||||
{
|
||||
foreach (var score in realm.All<ScoreInfo>().Where(score => score.BeatmapHash == beatmap.Hash))
|
||||
score.BeatmapInfo = beatmap;
|
||||
}
|
||||
|
||||
ProcessBeatmap?.Invoke(model, parameters.Batch ? MetadataLookupScope.LocalCacheFirst : MetadataLookupScope.OnlineFirst);
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@ namespace osu.Game.Collections
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
X = -OsuScrollContainer.SCROLL_BAR_HEIGHT,
|
||||
X = -OsuScrollContainer.SCROLL_BAR_WIDTH,
|
||||
Scale = new Vector2(0.65f),
|
||||
Action = addOrRemove,
|
||||
});
|
||||
|
@ -3,10 +3,12 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using AutoMapper;
|
||||
using AutoMapper.Internal;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Models;
|
||||
@ -52,10 +54,23 @@ namespace osu.Game.Database
|
||||
{
|
||||
foreach (var beatmap in s.Beatmaps)
|
||||
{
|
||||
var existing = d.Beatmaps.FirstOrDefault(b => b.ID == beatmap.ID);
|
||||
// Importantly, search all of realm for the beatmap (not just the set's beatmaps).
|
||||
// It may have gotten detached, and if that's the case let's use this opportunity to fix
|
||||
// things up.
|
||||
var existingBeatmap = d.Realm.Find<BeatmapInfo>(beatmap.ID);
|
||||
|
||||
if (existing != null)
|
||||
copyChangesToRealm(beatmap, existing);
|
||||
if (existingBeatmap != null)
|
||||
{
|
||||
// As above, reattach if it happens to not be in the set's beatmaps.
|
||||
if (!d.Beatmaps.Contains(existingBeatmap))
|
||||
{
|
||||
Debug.Fail("Beatmaps should never become detached under normal circumstances. If this ever triggers, it should be investigated further.");
|
||||
Logger.Log("WARNING: One of the difficulties in a beatmap was detached from its set. Please save a copy of logs and report this to devs.", LoggingTarget.Database, LogLevel.Important);
|
||||
d.Beatmaps.Add(existingBeatmap);
|
||||
}
|
||||
|
||||
copyChangesToRealm(beatmap, existingBeatmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newBeatmap = new BeatmapInfo
|
||||
@ -64,6 +79,7 @@ namespace osu.Game.Database
|
||||
BeatmapSet = d,
|
||||
Ruleset = d.Realm.Find<RulesetInfo>(beatmap.Ruleset.ShortName)
|
||||
};
|
||||
|
||||
d.Beatmaps.Add(newBeatmap);
|
||||
copyChangesToRealm(beatmap, newBeatmap);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
public partial class OsuScrollContainer<T> : ScrollContainer<T> where T : Drawable
|
||||
{
|
||||
public const float SCROLL_BAR_HEIGHT = 10;
|
||||
public const float SCROLL_BAR_WIDTH = 10;
|
||||
public const float SCROLL_BAR_PADDING = 3;
|
||||
|
||||
/// <summary>
|
||||
@ -139,6 +139,8 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
private readonly Box box;
|
||||
|
||||
protected override float MinimumDimSize => SCROLL_BAR_WIDTH * 3;
|
||||
|
||||
public OsuScrollbar(Direction scrollDir)
|
||||
: base(scrollDir)
|
||||
{
|
||||
@ -147,7 +149,7 @@ namespace osu.Game.Graphics.Containers
|
||||
CornerRadius = 5;
|
||||
|
||||
// needs to be set initially for the ResizeTo to respect minimum size
|
||||
Size = new Vector2(SCROLL_BAR_HEIGHT);
|
||||
Size = new Vector2(SCROLL_BAR_WIDTH);
|
||||
|
||||
const float margin = 3;
|
||||
|
||||
@ -173,11 +175,10 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None)
|
||||
{
|
||||
Vector2 size = new Vector2(SCROLL_BAR_HEIGHT)
|
||||
this.ResizeTo(new Vector2(SCROLL_BAR_WIDTH)
|
||||
{
|
||||
[(int)ScrollDirection] = val
|
||||
};
|
||||
this.ResizeTo(size, duration, easing);
|
||||
}, duration, easing);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
|
@ -24,6 +24,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString SignedIn => new TranslatableString(getKey(@"signed_in"), @"Signed in");
|
||||
|
||||
/// <summary>
|
||||
/// "Sign out"
|
||||
/// </summary>
|
||||
public static LocalisableString SignOut => new TranslatableString(getKey(@"sign_out"), @"Sign out");
|
||||
|
||||
/// <summary>
|
||||
/// "Account"
|
||||
/// </summary>
|
||||
|
@ -88,6 +88,16 @@ Please try changing your audio device to a working setting.");
|
||||
/// </summary>
|
||||
public static LocalisableString LinkTypeNotSupported => new TranslatableString(getKey(@"unsupported_link_type"), @"This link type is not yet supported!");
|
||||
|
||||
/// <summary>
|
||||
/// "You received a private message from '{0}'. Click to read it!"
|
||||
/// </summary>
|
||||
public static LocalisableString PrivateMessageReceived(string username) => new TranslatableString(getKey(@"private_message_received"), @"You received a private message from '{0}'. Click to read it!", username);
|
||||
|
||||
/// <summary>
|
||||
/// "Your name was mentioned in chat by '{0}'. Click to find out why!"
|
||||
/// </summary>
|
||||
public static LocalisableString YourNameWasMentioned(string username) => new TranslatableString(getKey(@"your_name_was_mentioned"), @"Your name was mentioned in chat by '{0}'. Click to find out why!", username);
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
@ -154,7 +155,7 @@ namespace osu.Game.Online.Chat
|
||||
: base(message, channel)
|
||||
{
|
||||
Icon = FontAwesome.Solid.Envelope;
|
||||
Text = $"You received a private message from '{message.Sender.Username}'. Click to read it!";
|
||||
Text = NotificationsStrings.PrivateMessageReceived(message.Sender.Username);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +165,7 @@ namespace osu.Game.Online.Chat
|
||||
: base(message, channel)
|
||||
{
|
||||
Icon = FontAwesome.Solid.At;
|
||||
Text = $"Your name was mentioned in chat by '{message.Sender.Username}'. Click to find out why!";
|
||||
Text = NotificationsStrings.YourNameWasMentioned(message.Sender.Username);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1164,7 +1164,9 @@ namespace osu.Game
|
||||
private void forwardTabletLogsToNotifications()
|
||||
{
|
||||
const string tablet_prefix = @"[Tablet] ";
|
||||
|
||||
bool notifyOnWarning = true;
|
||||
bool notifyOnError = true;
|
||||
|
||||
Logger.NewEntry += entry =>
|
||||
{
|
||||
@ -1175,6 +1177,11 @@ namespace osu.Game
|
||||
|
||||
if (entry.Level == LogLevel.Error)
|
||||
{
|
||||
if (!notifyOnError)
|
||||
return;
|
||||
|
||||
notifyOnError = false;
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
Notifications.Post(new SimpleNotification
|
||||
@ -1213,7 +1220,11 @@ namespace osu.Game
|
||||
Schedule(() =>
|
||||
{
|
||||
ITabletHandler tablet = Host.AvailableInputHandlers.OfType<ITabletHandler>().SingleOrDefault();
|
||||
tablet?.Tablet.BindValueChanged(_ => notifyOnWarning = true, true);
|
||||
tablet?.Tablet.BindValueChanged(_ =>
|
||||
{
|
||||
notifyOnWarning = true;
|
||||
notifyOnError = true;
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -185,7 +185,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
OnHovered = beatmap =>
|
||||
{
|
||||
showBeatmap(beatmap);
|
||||
starRating.Text = beatmap.StarRating.ToLocalisableString(@"0.##");
|
||||
starRating.Text = beatmap.StarRating.ToLocalisableString(@"0.00");
|
||||
starRatingContainer.FadeIn(100);
|
||||
},
|
||||
OnClicked = beatmap => { Beatmap.Value = beatmap; },
|
||||
|
@ -44,6 +44,6 @@ namespace osu.Game.Overlays
|
||||
/// <summary>
|
||||
/// All ongoing operations (ie. any <see cref="ProgressNotification"/> not in a completed state).
|
||||
/// </summary>
|
||||
public IEnumerable<ProgressNotification> OngoingOperations => AllNotifications.OfType<ProgressNotification>().Where(p => p.State != ProgressNotificationState.Completed);
|
||||
public IEnumerable<ProgressNotification> OngoingOperations => AllNotifications.OfType<ProgressNotification>().Where(p => p.State != ProgressNotificationState.Completed && p.State != ProgressNotificationState.Cancelled);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Login
|
||||
[LocalisableDescription(typeof(LoginPanelStrings), nameof(LoginPanelStrings.AppearOffline))]
|
||||
AppearOffline,
|
||||
|
||||
[LocalisableDescription(typeof(UserVerificationStrings), nameof(UserVerificationStrings.BoxInfoLogoutLink))]
|
||||
[LocalisableDescription(typeof(LoginPanelStrings), nameof(LoginPanelStrings.SignOut))]
|
||||
SignOut,
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Overlays
|
||||
scrollbarBackground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = OsuScrollContainer.SCROLL_BAR_HEIGHT,
|
||||
Width = OsuScrollContainer.SCROLL_BAR_WIDTH,
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Alpha = 0.5f
|
||||
|
@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
public virtual bool DisplayResult => true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
|
||||
/// The scoring result of this <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
public bool AllJudged => Judged && NestedHitObjects.All(h => h.AllJudged);
|
||||
public JudgementResult Result => Entry?.Result;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="DrawableHitObject"/> has been hit. This occurs if <see cref="Result"/> is hit.
|
||||
@ -112,12 +112,12 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// Whether this <see cref="DrawableHitObject"/> has been judged.
|
||||
/// Note: This does NOT include nested hitobjects.
|
||||
/// </summary>
|
||||
public bool Judged => Result?.HasResult ?? true;
|
||||
public bool Judged => Entry?.Judged ?? true;
|
||||
|
||||
/// <summary>
|
||||
/// The scoring result of this <see cref="DrawableHitObject"/>.
|
||||
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
|
||||
/// </summary>
|
||||
public JudgementResult Result => Entry?.Result;
|
||||
public bool AllJudged => Entry?.AllJudged ?? true;
|
||||
|
||||
/// <summary>
|
||||
/// The relative X position of this hit object for sample playback balance adjustment.
|
||||
@ -218,6 +218,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
protected sealed override void OnApply(HitObjectLifetimeEntry entry)
|
||||
{
|
||||
Debug.Assert(Entry != null);
|
||||
|
||||
// LifetimeStart is already computed using HitObjectLifetimeEntry's InitialLifetimeOffset.
|
||||
// We override this with DHO's InitialLifetimeOffset for a non-pooled DHO.
|
||||
if (entry is SyntheticHitObjectEntry)
|
||||
@ -247,6 +249,12 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
drawableNested.ParentHitObject = this;
|
||||
|
||||
nestedHitObjects.Add(drawableNested);
|
||||
|
||||
// assume that synthetic entries are not pooled and therefore need to be managed from within the DHO.
|
||||
// this is important for the correctness of value of flags such as `AllJudged`.
|
||||
if (drawableNested.Entry is SyntheticHitObjectEntry syntheticNestedEntry)
|
||||
Entry.NestedEntries.Add(syntheticNestedEntry);
|
||||
|
||||
AddNestedHitObject(drawableNested);
|
||||
}
|
||||
|
||||
@ -290,6 +298,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
protected sealed override void OnFree(HitObjectLifetimeEntry entry)
|
||||
{
|
||||
Debug.Assert(Entry != null);
|
||||
|
||||
StartTimeBindable.UnbindFrom(HitObject.StartTimeBindable);
|
||||
|
||||
if (HitObject is IHasComboInformation combo)
|
||||
@ -318,6 +328,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
}
|
||||
|
||||
nestedHitObjects.Clear();
|
||||
// clean up synthetic entries manually added in `Apply()`.
|
||||
Entry.NestedEntries.RemoveAll(nestedEntry => nestedEntry is SyntheticHitObjectEntry);
|
||||
ClearNestedHitObjects();
|
||||
|
||||
HitObject.DefaultsApplied -= onDefaultsApplied;
|
||||
|
@ -2,6 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@ -19,12 +21,28 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// </summary>
|
||||
public readonly HitObject HitObject;
|
||||
|
||||
/// <summary>
|
||||
/// The list of <see cref="HitObjectLifetimeEntry"/> for the <see cref="HitObject"/>'s nested objects (if any).
|
||||
/// </summary>
|
||||
public List<HitObjectLifetimeEntry> NestedEntries { get; internal set; } = new List<HitObjectLifetimeEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// The result that <see cref="HitObject"/> was judged with.
|
||||
/// This is set by the accompanying <see cref="DrawableHitObject"/>, and reused when required for rewinding.
|
||||
/// </summary>
|
||||
internal JudgementResult? Result;
|
||||
|
||||
/// <summary>
|
||||
/// Whether <see cref="HitObject"/> has been judged.
|
||||
/// Note: This does NOT include nested hitobjects.
|
||||
/// </summary>
|
||||
public bool Judged => Result?.HasResult ?? true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether <see cref="HitObject"/> and all of its nested objects have been judged.
|
||||
/// </summary>
|
||||
public bool AllJudged => Judged && NestedEntries.All(h => h.AllJudged);
|
||||
|
||||
private readonly IBindable<double> startTimeBindable = new BindableDouble();
|
||||
|
||||
internal event Action? RevertResult;
|
||||
|
@ -43,11 +43,6 @@ namespace osu.Game.Rulesets.Objects.Pooling
|
||||
/// </remarks>
|
||||
private readonly Dictionary<HitObjectLifetimeEntry, HitObject> parentMap = new Dictionary<HitObjectLifetimeEntry, HitObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Stores the list of child entries for each hit object managed by this <see cref="HitObjectEntryManager"/>.
|
||||
/// </summary>
|
||||
private readonly Dictionary<HitObject, List<HitObjectLifetimeEntry>> childrenMap = new Dictionary<HitObject, List<HitObjectLifetimeEntry>>();
|
||||
|
||||
public void Add(HitObjectLifetimeEntry entry, HitObject? parent)
|
||||
{
|
||||
HitObject hitObject = entry.HitObject;
|
||||
@ -57,22 +52,24 @@ namespace osu.Game.Rulesets.Objects.Pooling
|
||||
|
||||
// Add the entry.
|
||||
entryMap[hitObject] = entry;
|
||||
childrenMap[hitObject] = new List<HitObjectLifetimeEntry>();
|
||||
|
||||
// If the entry has a parent, set it and add the entry to the parent's children.
|
||||
if (parent != null)
|
||||
{
|
||||
parentMap[entry] = parent;
|
||||
if (childrenMap.TryGetValue(parent, out var parentChildEntries))
|
||||
parentChildEntries.Add(entry);
|
||||
if (entryMap.TryGetValue(parent, out var parentEntry))
|
||||
parentEntry.NestedEntries.Add(entry);
|
||||
}
|
||||
|
||||
hitObject.DefaultsApplied += onDefaultsApplied;
|
||||
OnEntryAdded?.Invoke(entry, parent);
|
||||
}
|
||||
|
||||
public void Remove(HitObjectLifetimeEntry entry)
|
||||
public bool Remove(HitObjectLifetimeEntry entry)
|
||||
{
|
||||
if (entry is SyntheticHitObjectEntry)
|
||||
return false;
|
||||
|
||||
HitObject hitObject = entry.HitObject;
|
||||
|
||||
if (!entryMap.ContainsKey(hitObject))
|
||||
@ -81,18 +78,16 @@ namespace osu.Game.Rulesets.Objects.Pooling
|
||||
entryMap.Remove(hitObject);
|
||||
|
||||
// If the entry has a parent, unset it and remove the entry from the parents' children.
|
||||
if (parentMap.Remove(entry, out var parent) && childrenMap.TryGetValue(parent, out var parentChildEntries))
|
||||
parentChildEntries.Remove(entry);
|
||||
if (parentMap.Remove(entry, out var parent) && entryMap.TryGetValue(parent, out var parentEntry))
|
||||
parentEntry.NestedEntries.Remove(entry);
|
||||
|
||||
// Remove all the entries' children.
|
||||
if (childrenMap.Remove(hitObject, out var childEntries))
|
||||
{
|
||||
foreach (var childEntry in childEntries)
|
||||
foreach (var childEntry in entry.NestedEntries)
|
||||
Remove(childEntry);
|
||||
}
|
||||
|
||||
hitObject.DefaultsApplied -= onDefaultsApplied;
|
||||
OnEntryRemoved?.Invoke(entry, parent);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGet(HitObject hitObject, [MaybeNullWhen(false)] out HitObjectLifetimeEntry entry)
|
||||
@ -105,16 +100,16 @@ namespace osu.Game.Rulesets.Objects.Pooling
|
||||
/// </summary>
|
||||
private void onDefaultsApplied(HitObject hitObject)
|
||||
{
|
||||
if (!childrenMap.Remove(hitObject, out var childEntries))
|
||||
if (!entryMap.TryGetValue(hitObject, out var entry))
|
||||
return;
|
||||
|
||||
// Remove all the entries' children. At this point the parents' (this entries') children list has been removed from the map, so this does not cause upwards traversal.
|
||||
foreach (var entry in childEntries)
|
||||
Remove(entry);
|
||||
// Replace the entire list rather than clearing to prevent circular traversal later.
|
||||
var previousEntries = entry.NestedEntries;
|
||||
entry.NestedEntries = new List<HitObjectLifetimeEntry>();
|
||||
|
||||
// The removed children list needs to be added back to the map for the entry to potentially receive children.
|
||||
childEntries.Clear();
|
||||
childrenMap[hitObject] = childEntries;
|
||||
// Remove all the entries' children. At this point the parents' (this entries') children list has been reconstructed, so this does not cause upwards traversal.
|
||||
foreach (var nested in previousEntries)
|
||||
Remove(nested);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ namespace osu.Game.Scoring
|
||||
{
|
||||
Ruleset = ruleset ?? new RulesetInfo();
|
||||
BeatmapInfo = beatmap ?? new BeatmapInfo();
|
||||
BeatmapHash = BeatmapInfo.Hash;
|
||||
RealmUser = realmUser ?? new RealmUser();
|
||||
ID = Guid.NewGuid();
|
||||
}
|
||||
|
@ -377,10 +377,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
float leftExcess = thisQuad.TopLeft.X - parentQuad.TopLeft.X;
|
||||
float rightExcess = parentQuad.TopRight.X - thisQuad.TopRight.X;
|
||||
|
||||
if (topExcess + bottomExcess < buttons.Height + button_padding)
|
||||
float minHeight = buttons.ScreenSpaceDrawQuad.Height;
|
||||
|
||||
if (topExcess < minHeight && bottomExcess < minHeight)
|
||||
{
|
||||
buttons.Anchor = Anchor.BottomCentre;
|
||||
buttons.Origin = Anchor.BottomCentre;
|
||||
buttons.Y = Math.Min(0, ToLocalSpace(Parent.ScreenSpaceDrawQuad.BottomLeft).Y - DrawHeight);
|
||||
}
|
||||
else if (topExcess > bottomExcess)
|
||||
{
|
||||
|
@ -160,13 +160,23 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
bool handled;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.EditorFlipHorizontally:
|
||||
return HandleFlip(Direction.Horizontal, true);
|
||||
ChangeHandler?.BeginChange();
|
||||
handled = HandleFlip(Direction.Horizontal, true);
|
||||
ChangeHandler?.EndChange();
|
||||
|
||||
return handled;
|
||||
|
||||
case GlobalAction.EditorFlipVertically:
|
||||
return HandleFlip(Direction.Vertical, true);
|
||||
ChangeHandler?.BeginChange();
|
||||
handled = HandleFlip(Direction.Vertical, true);
|
||||
ChangeHandler?.EndChange();
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
scroll.RelativeSizeAxes = Axes.X;
|
||||
scroll.Height = ParticipantsList.TILE_SIZE + OsuScrollContainer.SCROLL_BAR_HEIGHT + OsuScrollContainer.SCROLL_BAR_PADDING * 2;
|
||||
scroll.Height = ParticipantsList.TILE_SIZE + OsuScrollContainer.SCROLL_BAR_WIDTH + OsuScrollContainer.SCROLL_BAR_PADDING * 2;
|
||||
|
||||
list.RelativeSizeAxes = Axes.Y;
|
||||
list.AutoSizeAxes = Axes.X;
|
||||
|
Loading…
Reference in New Issue
Block a user