Merge branch 'master' into show-loved-pp

This commit is contained in:
Dean Herbert
2020-03-11 01:08:13 +09:00
committed by GitHub
267 changed files with 5567 additions and 2368 deletions

View File

@ -9,6 +9,8 @@ about: Issues regarding encountered bugs.
**osu!lazer version:** **osu!lazer version:**
**Logs:** **Logs:**
<!--
*please attach logs here, which are located at:* *please attach logs here, which are located at:*
- `%AppData%/osu/logs` *(on Windows),* - `%AppData%/osu/logs` *(on Windows),*
- `~/.local/share/osu/logs` *(on Linux & macOS).* - `~/.local/share/osu/logs` *(on Linux & macOS).*
-->

View File

@ -9,8 +9,10 @@ about: Issues regarding crashes or permanent freezes.
**osu!lazer version:** **osu!lazer version:**
**Logs:** **Logs:**
<!--
*please attach logs here, which are located at:* *please attach logs here, which are located at:*
- `%AppData%/osu/logs` *(on Windows),* - `%AppData%/osu/logs` *(on Windows),*
- `~/.local/share/osu/logs` *(on Linux & macOS).* - `~/.local/share/osu/logs` *(on Linux & macOS).*
-->
**Computer Specifications:** **Computer Specifications:**

40
.vscode/launch.json vendored
View File

@ -11,11 +11,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)", "preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -28,11 +23,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)", "preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -45,11 +35,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Debug)", "preLaunchTask": "Build tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -62,11 +47,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Release)", "preLaunchTask": "Build tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -80,11 +60,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)", "preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -98,11 +73,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)", "preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -116,11 +86,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tournament tests (Debug)", "preLaunchTask": "Build tournament tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {
@ -134,11 +99,6 @@
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "Build tournament tests (Release)", "preLaunchTask": "Build tournament tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.1:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole" "console": "internalConsole"
}, },
{ {

View File

@ -27,7 +27,7 @@ If you are looking to install or test osu! without setting up a development envi
**Latest build:** **Latest build:**
| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.x86_64.AppImage) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) | [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
| ------------- | ------------- | ------------- | ------------- | ------------- | | ------------- | ------------- | ------------- | ------------- | ------------- |
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore31&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs. - When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore31&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.

View File

@ -25,7 +25,6 @@
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
<Optimize>False</Optimize> <Optimize>False</Optimize>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<EnableLLVM>false</EnableLLVM>
<AndroidManagedSymbols>false</AndroidManagedSymbols> <AndroidManagedSymbols>false</AndroidManagedSymbols>
<AndroidUseSharedRuntime>true</AndroidUseSharedRuntime> <AndroidUseSharedRuntime>true</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>false</EmbedAssembliesIntoApk> <EmbedAssembliesIntoApk>false</EmbedAssembliesIntoApk>
@ -34,7 +33,6 @@
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType> <DebugType>None</DebugType>
<Optimize>True</Optimize> <Optimize>True</Optimize>
<EnableLLVM>true</EnableLLVM>
<AndroidManagedSymbols>false</AndroidManagedSymbols> <AndroidManagedSymbols>false</AndroidManagedSymbols>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime> <AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk> <EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
@ -53,7 +51,7 @@
<Reference Include="Java.Interop" /> <Reference Include="Java.Interop" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.221.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.304.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.221.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2020.310.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -30,11 +30,6 @@ namespace osu.Android
} }
} }
protected override void LoadComplete() protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
{
base.LoadComplete();
Add(new SimpleUpdateManager());
}
} }
} }

View File

@ -13,6 +13,7 @@
<AssemblyName>osu.Android</AssemblyName> <AssemblyName>osu.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest> <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis> <AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
<EnableLLVM>false</EnableLLVM> <!-- This currently causes random lockups during gameplay. https://github.com/mono/mono/issues/18973 -->
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n> <MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>

View File

@ -47,20 +47,25 @@ namespace osu.Desktop
return null; return null;
} }
protected override UpdateManager CreateUpdateManager()
{
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.Windows:
return new SquirrelUpdateManager();
default:
return new SimpleUpdateManager();
}
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
if (!noVersionOverlay) if (!noVersionOverlay)
{
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, Add); LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, Add);
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
Add(new SquirrelUpdateManager());
else
Add(new SimpleUpdateManager());
}
LoadComponentAsync(new DiscordRichPresence(), Add); LoadComponentAsync(new DiscordRichPresence(), Add);
} }

View File

@ -0,0 +1,54 @@
// 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 NUnit.Framework;
using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Catch.Tests.Mods
{
public class TestSceneCatchModPerfect : ModPerfectTestScene
{
public TestSceneCatchModPerfect()
: base(new CatchRuleset(), new CatchModPerfect())
{
}
[TestCase(false)]
[TestCase(true)]
public void TestBananaShower(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new BananaShower { StartTime = 1000, EndTime = 3000 }, false), shouldMiss);
[TestCase(false)]
[TestCase(true)]
public void TestFruit(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Fruit { StartTime = 1000 }), shouldMiss);
[TestCase(false)]
[TestCase(true)]
public void TestJuiceStream(bool shouldMiss)
{
var stream = new JuiceStream
{
StartTime = 1000,
Path = new SliderPath(PathType.Linear, new[]
{
Vector2.Zero,
new Vector2(100, 0),
})
};
CreateHitObjectTest(new HitObjectTestData(stream), shouldMiss);
}
// We only care about testing misses, hits are tested via JuiceStream
[TestCase(true)]
public void TestDroplet(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Droplet { StartTime = 1000 }), shouldMiss);
// We only care about testing misses, hits are tested via JuiceStream
[TestCase(true)]
public void TestTinyDroplet(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new TinyDroplet { StartTime = 1000 }), shouldMiss);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

View File

@ -7,7 +7,6 @@ using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using osuTK; using osuTK;
@ -51,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Tests
return beatmap; return beatmap;
} }
protected override Player CreatePlayer(Ruleset ruleset) protected override TestPlayer CreatePlayer(Ruleset ruleset)
{ {
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return base.CreatePlayer(ruleset); return base.CreatePlayer(ruleset);

View File

@ -29,6 +29,12 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
[Test]
public void TestBananaShower()
{
AddUntilStep("player is done", () => !Player.ValidForResume);
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
@ -40,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.Tests
} }
}; };
beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 5000, NewCombo = true }); beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 3000, NewCombo = true });
return beatmap; return beatmap;
} }

View File

@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
typeof(CatcherArea), typeof(CatcherArea),
typeof(CatcherSprite)
}; };
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -42,6 +42,9 @@ namespace osu.Game.Rulesets.Catch.Tests
AddStep("show droplet", () => SetContents(createDrawableDroplet)); AddStep("show droplet", () => SetContents(createDrawableDroplet));
AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet)); AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet));
foreach (FruitVisualRepresentation rep in Enum.GetValues(typeof(FruitVisualRepresentation)))
AddStep($"show hyperdash {rep}", () => SetContents(() => createDrawable(rep, true)));
} }
private Drawable createDrawableTinyDroplet() private Drawable createDrawableTinyDroplet()
@ -82,9 +85,13 @@ namespace osu.Game.Rulesets.Catch.Tests
}; };
} }
private Drawable createDrawable(FruitVisualRepresentation rep) private Drawable createDrawable(FruitVisualRepresentation rep, bool hyperdash = false)
{ {
Fruit fruit = new TestCatchFruit(rep) { Scale = 1.5f }; Fruit fruit = new TestCatchFruit(rep)
{
Scale = 1.5f,
HyperDashTarget = hyperdash ? new Banana() : null
};
return new DrawableFruit(fruit) return new DrawableFruit(fruit)
{ {

View File

@ -1,9 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
@ -22,7 +26,16 @@ namespace osu.Game.Rulesets.Catch.Tests
public void TestHyperDash() public void TestHyperDash()
{ {
AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash); AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);
AddUntilStep("wait for left hyperdash", () => getCatcher().Scale.X < 0 && getCatcher().HyperDashing);
for (int i = 0; i < 2; i++)
{
AddUntilStep("wait for right hyperdash", () => getCatcher().Scale.X > 0 && getCatcher().HyperDashing);
AddUntilStep("wait for left hyperdash", () => getCatcher().Scale.X < 0 && getCatcher().HyperDashing);
} }
}
private CatcherArea.Catcher getCatcher() => Player.ChildrenOfType<CatcherArea>().First().MovableCatcher;
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{ {
@ -35,17 +48,40 @@ namespace osu.Game.Rulesets.Catch.Tests
} }
}; };
// Should produce a hyper-dash // Should produce a hyper-dash (edge case test)
beatmap.HitObjects.Add(new Fruit { StartTime = 816, X = 308 / 512f, NewCombo = true }); beatmap.HitObjects.Add(new Fruit { StartTime = 1816, X = 308 / 512f, NewCombo = true });
beatmap.HitObjects.Add(new Fruit { StartTime = 1008, X = 56 / 512f, }); beatmap.HitObjects.Add(new JuiceStream { StartTime = 2008, X = 56 / 512f, });
for (int i = 0; i < 512; i++) double startTime = 3000;
{
if (i % 5 < 3) const float left_x = 0.02f;
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = 2000 + i * 100, NewCombo = i % 8 == 0 }); const float right_x = 0.98f;
}
createObjects(() => new Fruit(), left_x);
createObjects(() => new JuiceStream(), right_x);
createObjects(() => new JuiceStream(), left_x);
createObjects(() => new Fruit(), right_x);
createObjects(() => new Fruit(), left_x);
createObjects(() => new Fruit(), right_x);
createObjects(() => new JuiceStream(), left_x);
return beatmap; return beatmap;
void createObjects(Func<CatchHitObject> createObject, float x)
{
const float spacing = 140;
for (int i = 0; i < 3; i++)
{
var hitObject = createObject();
hitObject.X = x;
hitObject.StartTime = startTime + i * spacing;
beatmap.HitObjects.Add(hitObject);
}
startTime += 700;
}
} }
} }
} }

View File

@ -0,0 +1,56 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Catch.Tests
{
public class TestSceneJuiceStream : PlayerTestScene
{
public TestSceneJuiceStream()
: base(new CatchRuleset())
{
}
[Test]
public void TestJuiceStreamEndingCombo()
{
AddUntilStep("player is done", () => !Player.ValidForResume);
}
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 5, SliderMultiplier = 2 },
Ruleset = ruleset
},
HitObjects = new List<HitObject>
{
new JuiceStream
{
X = 0.5f,
Path = new SliderPath(PathType.Linear, new[]
{
Vector2.Zero,
new Vector2(0, 100)
}),
StartTime = 200
},
new Banana
{
X = 0.5f,
StartTime = 1000,
NewCombo = true
}
}
};
}
}

View File

@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Catch
FruitGrapes, FruitGrapes,
FruitOrange, FruitOrange,
FruitPear, FruitPear,
Droplet Droplet,
CatcherIdle
} }
} }

View File

@ -1,11 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods
{ {
public class CatchModPerfect : ModPerfect public class CatchModPerfect : ModPerfect
{ {
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
=> !(result.Judgement is CatchBananaJudgement)
&& base.FailCondition(healthProcessor, result);
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Catch.Objects namespace osu.Game.Rulesets.Catch.Objects
@ -11,6 +12,8 @@ namespace osu.Game.Rulesets.Catch.Objects
public override bool LastInCombo => true; public override bool LastInCombo => true;
public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override void CreateNestedHitObjects() protected override void CreateNestedHitObjects()
{ {
base.CreateNestedHitObjects(); base.CreateNestedHitObjects();

View File

@ -7,9 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawables namespace osu.Game.Rulesets.Catch.Objects.Drawables
@ -64,15 +62,24 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
if (hitObject.HyperDash) if (hitObject.HyperDash)
{ {
AddInternal(new Pulp AddInternal(new Circle
{ {
RelativePositionAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
AccentColour = { Value = Color4.Red }, BorderColour = Color4.Red,
BorderThickness = 12f * RADIUS_ADJUST,
Children = new Drawable[]
{
new Box
{
AlwaysPresent = true,
Alpha = 0.3f,
Blending = BlendingParameters.Additive, Blending = BlendingParameters.Additive,
Alpha = 0.5f, RelativeSizeAxes = Axes.Both,
Scale = new Vector2(1.333f) Colour = Color4.Red,
}
}
}); });
} }
} }

View File

@ -7,6 +7,7 @@ using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -19,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.Objects
/// </summary> /// </summary>
private const float base_scoring_distance = 100; private const float base_scoring_distance = 100;
public override Judgement CreateJudgement() => new IgnoreJudgement();
public int RepeatCount { get; set; } public int RepeatCount { get; set; }
public double Velocity; public double Velocity;

View File

@ -44,6 +44,10 @@ namespace osu.Game.Rulesets.Catch.Skinning
return new LegacyFruitPiece("fruit-drop") { Scale = new Vector2(0.8f) }; return new LegacyFruitPiece("fruit-drop") { Scale = new Vector2(0.8f) };
break; break;
case CatchSkinComponents.CatcherIdle:
return this.GetAnimation("fruit-catcher-idle", true, true, true) ??
this.GetAnimation("fruit-ryuuta", true, true, true);
} }
return null; return null;

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Skinning namespace osu.Game.Rulesets.Catch.Skinning
@ -49,6 +50,23 @@ namespace osu.Game.Rulesets.Catch.Skinning
Origin = Anchor.Centre, Origin = Anchor.Centre,
}, },
}; };
if (drawableCatchObject.HitObject.HyperDash)
{
var hyperDash = new Sprite
{
Texture = skin.GetTexture(lookupName),
Colour = Color4.Red,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Blending = BlendingParameters.Additive,
Depth = 1,
Alpha = 0.7f,
Scale = new Vector2(1.2f)
};
AddInternal(hyperDash);
}
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -50,6 +50,9 @@ namespace osu.Game.Rulesets.Catch.UI
public void OnResult(DrawableCatchHitObject fruit, JudgementResult result) public void OnResult(DrawableCatchHitObject fruit, JudgementResult result)
{ {
if (result.Judgement is IgnoreJudgement)
return;
void runAfterLoaded(Action action) void runAfterLoaded(Action action)
{ {
if (lastPlateableFruit == null) if (lastPlateableFruit == null)
@ -89,7 +92,7 @@ namespace osu.Game.Rulesets.Catch.UI
if (fruit.HitObject.LastInCombo) if (fruit.HitObject.LastInCombo)
{ {
if (((CatchJudgement)result.Judgement).ShouldExplodeFor(result)) if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result))
runAfterLoaded(() => MovableCatcher.Explode()); runAfterLoaded(() => MovableCatcher.Explode());
else else
MovableCatcher.Drop(); MovableCatcher.Drop();
@ -152,7 +155,10 @@ namespace osu.Game.Rulesets.Catch.UI
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre, Origin = Anchor.BottomCentre,
}, },
createCatcherSprite(), createCatcherSprite().With(c =>
{
c.Anchor = Anchor.TopCentre;
})
}; };
} }
@ -202,12 +208,11 @@ namespace osu.Game.Rulesets.Catch.UI
var additive = createCatcherSprite(); var additive = createCatcherSprite();
additive.Anchor = Anchor; additive.Anchor = Anchor;
additive.OriginPosition += new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly.
additive.Position = Position;
additive.Scale = Scale; additive.Scale = Scale;
additive.Colour = HyperDashing ? Color4.Red : Color4.White; additive.Colour = HyperDashing ? Color4.Red : Color4.White;
additive.RelativePositionAxes = RelativePositionAxes;
additive.Blending = BlendingParameters.Additive; additive.Blending = BlendingParameters.Additive;
additive.RelativePositionAxes = RelativePositionAxes;
additive.Position = Position;
AdditiveTarget.Add(additive); AdditiveTarget.Add(additive);
@ -267,6 +272,10 @@ namespace osu.Game.Rulesets.Catch.UI
catchObjectPosition >= catcherPosition - halfCatchWidth && catchObjectPosition >= catcherPosition - halfCatchWidth &&
catchObjectPosition <= catcherPosition + halfCatchWidth; catchObjectPosition <= catcherPosition + halfCatchWidth;
// only update hyperdash state if we are catching a fruit.
// exceptions are Droplets and JuiceStreams.
if (!(fruit is Fruit)) return validCatch;
if (validCatch && fruit.HyperDash) if (validCatch && fruit.HyperDash)
{ {
var target = fruit.HyperDashTarget; var target = fruit.HyperDashTarget;

View File

@ -3,31 +3,35 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Catch.UI namespace osu.Game.Rulesets.Catch.UI
{ {
public class CatcherSprite : CompositeDrawable public class CatcherSprite : SkinnableDrawable
{ {
protected override bool ApplySizeRestrictionsToDefault => true;
public CatcherSprite() public CatcherSprite()
: base(new CatchSkinComponent(CatchSkinComponents.CatcherIdle), _ =>
new DefaultCatcherSprite(), confineMode: ConfineMode.ScaleDownToFit)
{ {
RelativeSizeAxes = Axes.None;
Size = new Vector2(CatcherArea.CATCHER_SIZE); Size = new Vector2(CatcherArea.CATCHER_SIZE);
// Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling. // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
OriginPosition = new Vector2(-0.02f, 0.06f) * CatcherArea.CATCHER_SIZE; OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE;
} }
private class DefaultCatcherSprite : Sprite
{
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load(TextureStore textures)
{ {
InternalChild = new SkinnableSprite("Gameplay/catch/fruit-catcher-idle", confineMode: ConfineMode.ScaleDownToFit) Texture = textures.Get("Gameplay/catch/fruit-catcher-idle");
{ }
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
};
} }
} }
} }

View File

@ -0,0 +1,26 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests.Mods
{
public class TestSceneManiaModPerfect : ModPerfectTestScene
{
public TestSceneManiaModPerfect()
: base(new ManiaRuleset(), new ManiaModPerfect())
{
}
[TestCase(false)]
[TestCase(true)]
public void TestNote(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Note { StartTime = 1000 }), shouldMiss);
[TestCase(false)]
[TestCase(true)]
public void TestHoldNote(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new HoldNote { StartTime = 1000, EndTime = 3000 }), shouldMiss);
}
}

View File

@ -1,14 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HoldNoteJudgement : ManiaJudgement
{
public override bool AffectsCombo => false;
protected override int NumericResultFor(HitResult result) => 0;
}
}

View File

@ -3,8 +3,8 @@
using System; using System;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Layout;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osuTK; using osuTK;
@ -22,21 +22,13 @@ namespace osu.Game.Rulesets.Mania.Mods
private class ManiaFlashlight : Flashlight private class ManiaFlashlight : Flashlight
{ {
private readonly Cached flashlightProperties = new Cached(); private readonly LayoutValue flashlightProperties = new LayoutValue(Invalidation.DrawSize);
public ManiaFlashlight() public ManiaFlashlight()
{ {
FlashlightSize = new Vector2(0, default_flashlight_size); FlashlightSize = new Vector2(0, default_flashlight_size);
}
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) AddLayout(flashlightProperties);
{
if ((invalidation & Invalidation.DrawSize) > 0)
{
flashlightProperties.Invalidate();
}
return base.Invalidate(invalidation, source, shallPropagate);
} }
protected override void Update() protected override void Update()

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Objects namespace osu.Game.Rulesets.Mania.Objects
@ -8,5 +9,7 @@ namespace osu.Game.Rulesets.Mania.Objects
public class BarLine : ManiaHitObject, IBarLine public class BarLine : ManiaHitObject, IBarLine
{ {
public bool Major { get; set; } public bool Major { get; set; }
public override Judgement CreateJudgement() => new IgnoreJudgement();
} }
} }

View File

@ -71,8 +71,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
} }
protected override void UpdateStateTransforms(ArmedState state) protected override void UpdateStateTransforms(ArmedState state) => this.FadeOut(150);
{
}
} }
} }

View File

@ -2,13 +2,13 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using osu.Framework.Caching;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Layout;
using osu.Game.Graphics; using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
@ -65,6 +65,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
} }
} }
}; };
AddLayout(subtractionCache);
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -100,15 +102,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
} }
} }
private readonly Cached subtractionCache = new Cached(); private readonly LayoutValue subtractionCache = new LayoutValue(Invalidation.DrawSize);
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) > 0)
subtractionCache.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void Update() protected override void Update()
{ {

View File

@ -4,7 +4,6 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -103,7 +102,7 @@ namespace osu.Game.Rulesets.Mania.Objects
} }
} }
public override Judgement CreateJudgement() => new HoldNoteJudgement(); public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty; protected override HitWindows CreateHitWindows() => HitWindows.Empty;
} }

View File

@ -115,9 +115,8 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Y = HIT_TARGET_POSITION + 150, Y = HIT_TARGET_POSITION + 150,
BypassAutoSizeAxes = Axes.Both
}, },
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both } topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
} }

View File

@ -0,0 +1,86 @@
// 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.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests.Mods
{
public class TestSceneOsuModDifficultyAdjust : ModTestScene
{
public TestSceneOsuModDifficultyAdjust()
: base(new OsuRuleset())
{
}
[Test]
public void TestNoAdjustment() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust(),
Autoplay = true,
PassCondition = checkSomeHit
});
[Test]
public void TestCircleSize1() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { CircleSize = { Value = 1 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsScale(0.78f)
});
[Test]
public void TestCircleSize10() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { CircleSize = { Value = 10 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsScale(0.15f)
});
[Test]
public void TestApproachRate1() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { ApproachRate = { Value = 1 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsPreempt(1680)
});
[Test]
public void TestApproachRate10() => CreateModTest(new ModTestData
{
Mod = new OsuModDifficultyAdjust { ApproachRate = { Value = 10 } },
Autoplay = true,
PassCondition = () => checkSomeHit() && checkObjectsPreempt(450)
});
private bool checkObjectsPreempt(double target)
{
var objects = Player.ChildrenOfType<DrawableHitCircle>();
if (!objects.Any())
return false;
return objects.All(o => o.HitObject.TimePreempt == target);
}
private bool checkObjectsScale(float target)
{
var objects = Player.ChildrenOfType<DrawableHitCircle>();
if (!objects.Any())
return false;
return objects.All(o => Precision.AlmostEquals(o.ChildrenOfType<ShakeContainer>().First().Children.OfType<Container>().Single().Scale.X, target));
}
private bool checkSomeHit()
{
return Player.ScoreProcessor.JudgedHits >= 2;
}
}
}

View File

@ -0,0 +1,35 @@
// 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 NUnit.Framework;
using osu.Framework.Utils;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests.Mods
{
public class TestSceneOsuModDoubleTime : ModTestScene
{
public TestSceneOsuModDoubleTime()
: base(new OsuRuleset())
{
}
[TestCase(0.5)]
[TestCase(1.01)]
[TestCase(1.5)]
[TestCase(2)]
[TestCase(5)]
public void TestSpeedChangeCustomisation(double rate)
{
var mod = new OsuModDoubleTime { SpeedChange = { Value = rate } };
CreateModTest(new ModTestData
{
Mod = mod,
PassCondition = () => Player.ScoreProcessor.JudgedHits >= 2 &&
Precision.AlmostEquals(Player.GameplayClockContainer.GameplayClock.Rate, mod.SpeedChange.Value)
});
}
}
}

View File

@ -0,0 +1,52 @@
// 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 NUnit.Framework;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests.Mods
{
public class TestSceneOsuModPerfect : ModPerfectTestScene
{
public TestSceneOsuModPerfect()
: base(new OsuRuleset(), new OsuModPerfect())
{
}
[TestCase(false)]
[TestCase(true)]
public void TestHitCircle(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new HitCircle { StartTime = 1000 }), shouldMiss);
[TestCase(false)]
[TestCase(true)]
public void TestSlider(bool shouldMiss)
{
var slider = new Slider
{
StartTime = 1000,
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
};
CreateHitObjectTest(new HitObjectTestData(slider), shouldMiss);
}
[TestCase(false)]
[TestCase(true)]
public void TestSpinner(bool shouldMiss)
{
var spinner = new Spinner
{
StartTime = 1000,
EndTime = 3000,
Position = new Vector2(256, 192)
};
CreateHitObjectTest(new HitObjectTestData(spinner), shouldMiss);
}
}
}

View File

@ -2,9 +2,12 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
@ -114,6 +117,22 @@ namespace osu.Game.Rulesets.Osu.Tests
assertGroups(); assertGroups();
} }
[Test]
public void TestStackedObjects()
{
addObjectsStep(() => new OsuHitObject[]
{
new HitCircle { Position = new Vector2(300, 100) },
new HitCircle
{
Position = new Vector2(300, 300),
StackHeight = 20
},
});
assertDirections();
}
private void addMultipleObjectsStep() => addObjectsStep(() => new OsuHitObject[] private void addMultipleObjectsStep() => addObjectsStep(() => new OsuHitObject[]
{ {
new HitCircle { Position = new Vector2(100, 100) }, new HitCircle { Position = new Vector2(100, 100) },
@ -207,6 +226,33 @@ namespace osu.Game.Rulesets.Osu.Tests
}); });
} }
private void assertDirections()
{
AddAssert("group directions are correct", () =>
{
for (int i = 0; i < hitObjectContainer.Count; i++)
{
DrawableOsuHitObject expectedStart = getObject(i);
DrawableOsuHitObject expectedEnd = i < hitObjectContainer.Count - 1 ? getObject(i + 1) : null;
if (expectedEnd == null)
continue;
var points = getGroup(i).ChildrenOfType<FollowPoint>().ToArray();
if (points.Length == 0)
continue;
float expectedDirection = MathF.Atan2(expectedStart.Position.Y - expectedEnd.Position.Y, expectedStart.Position.X - expectedEnd.Position.X);
float realDirection = MathF.Atan2(expectedStart.Position.Y - points[^1].Position.Y, expectedStart.Position.X - points[^1].Position.X);
if (!Precision.AlmostEquals(expectedDirection, realDirection))
throw new AssertionException($"Expected group {i} in direction {expectedDirection}, but was {realDirection}.");
}
return true;
});
}
private DrawableOsuHitObject getObject(int index) => hitObjectContainer[index]; private DrawableOsuHitObject getObject(int index) => hitObjectContainer[index];
private FollowPointConnection getGroup(int index) => followPointRenderer.Connections[index]; private FollowPointConnection getGroup(int index) => followPointRenderer.Connections[index];

View File

@ -0,0 +1,108 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestSceneHitCircleArea : ManualInputManagerTestScene
{
private HitCircle hitCircle;
private DrawableHitCircle drawableHitCircle;
private DrawableHitCircle.HitReceptor hitAreaReceptor => drawableHitCircle.HitArea;
[SetUp]
public new void SetUp()
{
base.SetUp();
Schedule(() =>
{
hitCircle = new HitCircle
{
Position = new Vector2(100, 100),
StartTime = Time.Current + 500
};
hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
Child = new SkinProvidingContainer(new DefaultSkin())
{
RelativeSizeAxes = Axes.Both,
Child = drawableHitCircle = new DrawableHitCircle(hitCircle)
{
Size = new Vector2(100)
}
};
});
}
[Test]
public void TestCircleHitCentre()
{
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(hitAreaReceptor.ScreenSpaceDrawQuad.Centre));
scheduleHit();
AddAssert("hit registered", () => hitAreaReceptor.HitAction == OsuAction.LeftButton);
}
[Test]
public void TestCircleHitLeftEdge()
{
AddStep("move mouse to left edge", () =>
{
var drawQuad = hitAreaReceptor.ScreenSpaceDrawQuad;
var mousePosition = new Vector2(drawQuad.TopLeft.X, drawQuad.Centre.Y);
InputManager.MoveMouseTo(mousePosition);
});
scheduleHit();
AddAssert("hit registered", () => hitAreaReceptor.HitAction == OsuAction.LeftButton);
}
[TestCase(0.95f, OsuAction.LeftButton)]
[TestCase(1.05f, null)]
public void TestHitsCloseToEdge(float relativeDistanceFromCentre, OsuAction? expectedAction)
{
AddStep("move mouse to top left circle edge", () =>
{
var drawQuad = hitAreaReceptor.ScreenSpaceDrawQuad;
// sqrt(2) / 2 = sin(45deg) = cos(45deg)
// draw width halved to get radius
float correction = relativeDistanceFromCentre * (float)Math.Sqrt(2) / 2 * (drawQuad.Width / 2);
var mousePosition = new Vector2(drawQuad.Centre.X - correction, drawQuad.Centre.Y - correction);
InputManager.MoveMouseTo(mousePosition);
});
scheduleHit();
AddAssert($"hit {(expectedAction == null ? "not " : string.Empty)}registered", () => hitAreaReceptor.HitAction == expectedAction);
}
[Test]
public void TestCircleMissBoundingBoxCorner()
{
AddStep("move mouse to top left corner of bounding box", () => InputManager.MoveMouseTo(hitAreaReceptor.ScreenSpaceDrawQuad.TopLeft));
scheduleHit();
AddAssert("hit not registered", () => hitAreaReceptor.HitAction == null);
}
private void scheduleHit() => AddStep("schedule action", () =>
{
var delay = hitCircle.StartTime - hitCircle.HitWindows.WindowFor(HitResult.Great) - Time.Current;
Scheduler.AddDelayed(() => hitAreaReceptor.OnPressed(OsuAction.LeftButton), delay);
});
}
}

View File

@ -0,0 +1,101 @@
// 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 NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Tests.Visual;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestSceneMissHitWindowJudgements : ModTestScene
{
public TestSceneMissHitWindowJudgements()
: base(new OsuRuleset())
{
}
[Test]
public void TestMissViaEarlyHit()
{
var beatmap = new Beatmap
{
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
};
var hitWindows = new OsuHitWindows();
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
CreateModTest(new ModTestData
{
Autoplay = false,
Mod = new TestAutoMod(),
Beatmap = new Beatmap
{
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
},
PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset < -hitWindows.WindowFor(HitResult.Meh) && Player.Results[0].Type == HitResult.Miss
});
}
[Test]
public void TestMissViaNotHitting()
{
var beatmap = new Beatmap
{
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
};
var hitWindows = new OsuHitWindows();
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
CreateModTest(new ModTestData
{
Autoplay = false,
Beatmap = beatmap,
PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset >= hitWindows.WindowFor(HitResult.Meh) && Player.Results[0].Type == HitResult.Miss
});
}
private class TestAutoMod : OsuModAutoplay
{
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "Autoplay" } },
Replay = new MissingAutoGenerator(beatmap).Generate()
};
}
private class MissingAutoGenerator : OsuAutoGeneratorBase
{
public new OsuBeatmap Beatmap => (OsuBeatmap)base.Beatmap;
public MissingAutoGenerator(IBeatmap beatmap)
: base(beatmap)
{
}
public override Replay Generate()
{
AddFrameToReplay(new OsuReplayFrame(-100000, new Vector2(256, 500)));
AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, new Vector2(256, 500)));
AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 1500, new Vector2(256, 500)));
AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 450, Beatmap.HitObjects[0].StackedPosition));
AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 350, Beatmap.HitObjects[0].StackedPosition, OsuAction.LeftButton));
AddFrameToReplay(new OsuReplayFrame(Beatmap.HitObjects[0].StartTime - 325, Beatmap.HitObjects[0].StackedPosition));
return Replay;
}
}
}
}

View File

@ -3,13 +3,13 @@
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Play; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestSceneOsuFlashlight : TestSceneOsuPlayer public class TestSceneOsuFlashlight : TestSceneOsuPlayer
{ {
protected override Player CreatePlayer(Ruleset ruleset) protected override TestPlayer CreatePlayer(Ruleset ruleset)
{ {
SelectedMods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), }; SelectedMods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), };

View File

@ -18,7 +18,6 @@ using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Screens.Play;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Storyboards; using osu.Game.Storyboards;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
@ -56,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private void checkNextHitObject(string skin) => private void checkNextHitObject(string skin) =>
AddUntilStep($"check skin from {skin}", () => AddUntilStep($"check skin from {skin}", () =>
{ {
var firstObject = ((TestPlayer)Player).DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.OfType<DrawableHitCircle>().FirstOrDefault(); var firstObject = Player.DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.OfType<DrawableHitCircle>().FirstOrDefault();
if (firstObject == null) if (firstObject == null)
return false; return false;
@ -75,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[Resolved] [Resolved]
private AudioManager audio { get; set; } private AudioManager audio { get; set; }
protected override Player CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin); protected override TestPlayer CreatePlayer(Ruleset ruleset) => new SkinProvidingPlayer(testUserSkin);
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, audio, testBeatmapSkin); protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, audio, testBeatmapSkin);

View File

@ -11,7 +11,6 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
using osuTK; using osuTK;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests
base.SetUpSteps(); base.SetUpSteps();
AddUntilStep("wait for track to start running", () => track.IsRunning); AddUntilStep("wait for track to start running", () => track.IsRunning);
AddStep("retrieve spinner", () => drawableSpinner = (DrawableSpinner)((TestPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.First()); AddStep("retrieve spinner", () => drawableSpinner = (DrawableSpinner)Player.DrawableRuleset.Playfield.AllHitObjects.First());
} }
[Test] [Test]
@ -89,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
AddStep($"seek to {time}", () => track.Seek(time)); AddStep($"seek to {time}", () => track.Seek(time));
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, ((TestPlayer)Player).DrawableRuleset.FrameStableClock.CurrentTime, 100)); AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
} }
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap

View File

@ -1,14 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Judgements
{
public class OsuSliderTailJudgement : OsuJudgement
{
public override bool AffectsCombo => false;
protected override int NumericResultFor(HitResult result) => 0;
}
}

View File

@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
private const int spacing = 32; private const int spacing = 32;
private const double preempt = 800; private const double preempt = 800;
public override bool RemoveWhenNotAlive => false;
/// <summary> /// <summary>
/// The start time of <see cref="Start"/>. /// The start time of <see cref="Start"/>.
/// </summary> /// </summary>
@ -79,27 +81,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
drawableObject.HitObject.DefaultsApplied += scheduleRefresh; drawableObject.HitObject.DefaultsApplied += scheduleRefresh;
} }
private void scheduleRefresh() => Scheduler.AddOnce(refresh); private void scheduleRefresh()
{
Scheduler.AddOnce(refresh);
}
private void refresh() private void refresh()
{ {
ClearInternal(); ClearInternal();
if (End == null)
return;
OsuHitObject osuStart = Start.HitObject; OsuHitObject osuStart = Start.HitObject;
OsuHitObject osuEnd = End.HitObject;
if (osuEnd.NewCombo)
return;
if (osuStart is Spinner || osuEnd is Spinner)
return;
Vector2 startPosition = osuStart.EndPosition;
Vector2 endPosition = osuEnd.Position;
double startTime = osuStart.GetEndTime(); double startTime = osuStart.GetEndTime();
LifetimeStart = startTime;
OsuHitObject osuEnd = End?.HitObject;
if (osuEnd == null || osuEnd.NewCombo || osuStart is Spinner || osuEnd is Spinner)
{
// ensure we always set a lifetime for full LifetimeManagementContainer benefits
LifetimeEnd = LifetimeStart;
return;
}
Vector2 startPosition = osuStart.StackedEndPosition;
Vector2 endPosition = osuEnd.StackedPosition;
double endTime = osuEnd.StartTime; double endTime = osuEnd.StartTime;
Vector2 distanceVector = endPosition - startPosition; Vector2 distanceVector = endPosition - startPosition;
@ -107,6 +113,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI)); float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI));
double duration = endTime - startTime; double duration = endTime - startTime;
double? firstTransformStartTime = null;
double finalTransformEndTime = startTime;
for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing) for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
{ {
float fraction = (float)d / distance; float fraction = (float)d / distance;
@ -125,16 +134,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
Scale = new Vector2(1.5f * osuEnd.Scale), Scale = new Vector2(1.5f * osuEnd.Scale),
}); });
if (firstTransformStartTime == null)
firstTransformStartTime = fadeInTime;
using (fp.BeginAbsoluteSequence(fadeInTime)) using (fp.BeginAbsoluteSequence(fadeInTime))
{ {
fp.FadeIn(osuEnd.TimeFadeIn); fp.FadeIn(osuEnd.TimeFadeIn);
fp.ScaleTo(osuEnd.Scale, osuEnd.TimeFadeIn, Easing.Out); fp.ScaleTo(osuEnd.Scale, osuEnd.TimeFadeIn, Easing.Out);
fp.MoveTo(pointEndPosition, osuEnd.TimeFadeIn, Easing.Out); fp.MoveTo(pointEndPosition, osuEnd.TimeFadeIn, Easing.Out);
fp.Delay(fadeOutTime - fadeInTime).FadeOut(osuEnd.TimeFadeIn); fp.Delay(fadeOutTime - fadeInTime).FadeOut(osuEnd.TimeFadeIn);
finalTransformEndTime = fadeOutTime + osuEnd.TimeFadeIn;
}
} }
fp.Expire(true); // todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed.
} LifetimeStart = firstTransformStartTime ?? startTime;
LifetimeEnd = finalTransformEndTime;
} }
} }
} }

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
/// <summary> /// <summary>
/// Visualises connections between <see cref="DrawableOsuHitObject"/>s. /// Visualises connections between <see cref="DrawableOsuHitObject"/>s.
/// </summary> /// </summary>
public class FollowPointRenderer : CompositeDrawable public class FollowPointRenderer : LifetimeManagementContainer
{ {
/// <summary> /// <summary>
/// All the <see cref="FollowPointConnection"/>s contained by this <see cref="FollowPointRenderer"/>. /// All the <see cref="FollowPointConnection"/>s contained by this <see cref="FollowPointRenderer"/>.
@ -45,8 +45,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
/// <returns>The index of <paramref name="connection"/> in <see cref="connections"/>.</returns> /// <returns>The index of <paramref name="connection"/> in <see cref="connections"/>.</returns>
private void addConnection(FollowPointConnection connection) private void addConnection(FollowPointConnection connection)
{ {
AddInternal(connection);
// Groups are sorted by their start time when added such that the index can be used to post-process other surrounding connections // Groups are sorted by their start time when added such that the index can be used to post-process other surrounding connections
int index = connections.AddInPlace(connection, Comparer<FollowPointConnection>.Create((g1, g2) => g1.StartTime.Value.CompareTo(g2.StartTime.Value))); int index = connections.AddInPlace(connection, Comparer<FollowPointConnection>.Create((g1, g2) => g1.StartTime.Value.CompareTo(g2.StartTime.Value)));
@ -74,6 +72,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
FollowPointConnection previousConnection = connections[index - 1]; FollowPointConnection previousConnection = connections[index - 1];
previousConnection.End = connection.Start; previousConnection.End = connection.Start;
} }
AddInternal(connection);
} }
/// <summary> /// <summary>

View File

@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public Drawable ProxiedLayer => ApproachCircle; public Drawable ProxiedLayer => ApproachCircle;
public class HitReceptor : Drawable, IKeyBindingHandler<OsuAction> public class HitReceptor : CompositeDrawable, IKeyBindingHandler<OsuAction>
{ {
// IsHovered is used // IsHovered is used
public override bool HandlePositionalInput => true; public override bool HandlePositionalInput => true;
@ -185,6 +185,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
CornerRadius = OsuHitObject.OBJECT_RADIUS;
CornerExponent = 2;
} }
public bool OnPressed(OsuAction action) public bool OnPressed(OsuAction action)

View File

@ -4,7 +4,6 @@
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects namespace osu.Game.Rulesets.Osu.Objects
@ -23,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects
pathVersion.BindValueChanged(_ => Position = slider.EndPosition); pathVersion.BindValueChanged(_ => Position = slider.EndPosition);
} }
public override Judgement CreateJudgement() => new OsuSliderTailJudgement(); public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty; protected override HitWindows CreateHitWindows() => HitWindows.Empty;
} }

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
new DifficultyRange(HitResult.Great, 80, 50, 20), new DifficultyRange(HitResult.Great, 80, 50, 20),
new DifficultyRange(HitResult.Good, 140, 100, 60), new DifficultyRange(HitResult.Good, 140, 100, 60),
new DifficultyRange(HitResult.Meh, 200, 150, 100), new DifficultyRange(HitResult.Meh, 200, 150, 100),
new DifficultyRange(HitResult.Miss, 200, 200, 200), new DifficultyRange(HitResult.Miss, 400, 400, 400),
}; };
public override bool IsHitResultAllowed(HitResult result) public override bool IsHitResultAllowed(HitResult result)

View File

@ -5,7 +5,6 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Batches; using osu.Framework.Graphics.Batches;
using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.OpenGL.Vertices;
@ -14,6 +13,7 @@ using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Layout;
using osu.Framework.Timing; using osu.Framework.Timing;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -43,6 +43,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
// -1 signals that the part is unusable, and should not be drawn // -1 signals that the part is unusable, and should not be drawn
parts[i].InvalidationID = -1; parts[i].InvalidationID = -1;
} }
AddLayout(partSizeCache);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -72,20 +74,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
} }
} }
private readonly Cached<Vector2> partSizeCache = new Cached<Vector2>(); private readonly LayoutValue<Vector2> partSizeCache = new LayoutValue<Vector2>(Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit | Invalidation.Presence);
private Vector2 partSize => partSizeCache.IsValid private Vector2 partSize => partSizeCache.IsValid
? partSizeCache.Value ? partSizeCache.Value
: (partSizeCache.Value = new Vector2(Texture.DisplayWidth, Texture.DisplayHeight) * DrawInfo.Matrix.ExtractScale().Xy); : (partSizeCache.Value = new Vector2(Texture.DisplayWidth, Texture.DisplayHeight) * DrawInfo.Matrix.ExtractScale().Xy);
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & (Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit | Invalidation.Presence)) > 0)
partSizeCache.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
/// <summary> /// <summary>
/// The amount of time to fade the cursor trail pieces. /// The amount of time to fade the cursor trail pieces.
/// </summary> /// </summary>
@ -97,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{ {
base.Update(); base.Update();
Invalidate(Invalidation.DrawNode, shallPropagate: false); Invalidate(Invalidation.DrawNode);
const int fade_clock_reset_threshold = 1000000; const int fade_clock_reset_threshold = 1000000;

View File

@ -0,0 +1,47 @@
// 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 NUnit.Framework;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Scoring;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests.Mods
{
public class TestSceneTaikoModPerfect : ModPerfectTestScene
{
public TestSceneTaikoModPerfect()
: base(new TestTaikoRuleset(), new TaikoModPerfect())
{
}
[TestCase(false)]
[TestCase(true)]
public void TestHit(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new CentreHit { StartTime = 1000 }), shouldMiss);
[TestCase(false)]
[TestCase(true)]
public void TestDrumRoll(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new DrumRoll { StartTime = 1000, EndTime = 3000 }), shouldMiss);
[TestCase(false)]
[TestCase(true)]
public void TestSwell(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Swell { StartTime = 1000, EndTime = 3000 }), shouldMiss);
private class TestTaikoRuleset : TaikoRuleset
{
public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new TestTaikoHealthProcessor();
private class TestTaikoHealthProcessor : TaikoHealthProcessor
{
protected override void Reset(bool storeResults)
{
base.Reset(storeResults);
Health.Value = 1; // Don't care about the health condition (only the mod condition)
}
}
}
}
}

View File

@ -1,24 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests namespace osu.Game.Rulesets.Taiko.Tests
{ {
public class TestSceneSwellJudgements : PlayerTestScene public class TestSceneSwellJudgements : PlayerTestScene
{ {
protected new TestPlayer Player => (TestPlayer)base.Player;
public TestSceneSwellJudgements() public TestSceneSwellJudgements()
: base(new TaikoRuleset()) : base(new TaikoRuleset())
{ {
@ -28,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
public void TestZeroTickTimeOffsets() public void TestZeroTickTimeOffsets()
{ {
AddUntilStep("gameplay finished", () => Player.ScoreProcessor.HasCompleted); AddUntilStep("gameplay finished", () => Player.ScoreProcessor.HasCompleted);
AddAssert("all tick offsets are 0", () => Player.Results.Where(r => r.Judgement is TaikoSwellTickJudgement).All(r => r.TimeOffset == 0)); AddAssert("all tick offsets are 0", () => Player.Results.Where(r => r.HitObject is SwellTick).All(r => r.TimeOffset == 0));
} }
protected override bool Autoplay => true; protected override bool Autoplay => true;
@ -50,25 +42,5 @@ namespace osu.Game.Rulesets.Taiko.Tests
return beatmap; return beatmap;
} }
protected override Player CreatePlayer(Ruleset ruleset) => new TestPlayer();
protected class TestPlayer : Player
{
public readonly List<JudgementResult> Results = new List<JudgementResult>();
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public TestPlayer()
: base(false, false)
{
}
[BackgroundDependencyLoader]
private void load()
{
ScoreProcessor.NewJudgement += r => Results.Add(r);
}
}
} }
} }

View File

@ -4,11 +4,9 @@
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests namespace osu.Game.Rulesets.Taiko.Tests
@ -22,10 +20,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
protected override bool AllowFail => true; protected override bool AllowFail => true;
protected override Player CreatePlayer(Ruleset ruleset) protected override TestPlayer CreatePlayer(Ruleset ruleset)
{ {
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray(); SelectedMods.Value = SelectedMods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray();
return new ScoreAccessiblePlayer(); return base.CreatePlayer(ruleset);
} }
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) =>
@ -49,20 +47,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
AddStep("Setup judgements", () => AddStep("Setup judgements", () =>
{ {
judged = false; judged = false;
((ScoreAccessiblePlayer)Player).ScoreProcessor.NewJudgement += b => judged = true; Player.ScoreProcessor.NewJudgement += b => judged = true;
}); });
AddUntilStep("swell judged", () => judged); AddUntilStep("swell judged", () => judged);
AddAssert("not failed", () => !Player.HasFailed); AddAssert("not failed", () => !Player.HasFailed);
} }
private class ScoreAccessiblePlayer : TestPlayer
{
public ScoreAccessiblePlayer()
: base(false, false)
{
}
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
}
} }
} }

View File

@ -2,8 +2,8 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Layout;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.Taiko.UI;
@ -30,13 +30,15 @@ namespace osu.Game.Rulesets.Taiko.Mods
private class TaikoFlashlight : Flashlight private class TaikoFlashlight : Flashlight
{ {
private readonly Cached flashlightProperties = new Cached(); private readonly LayoutValue flashlightProperties = new LayoutValue(Invalidation.DrawSize);
private readonly TaikoPlayfield taikoPlayfield; private readonly TaikoPlayfield taikoPlayfield;
public TaikoFlashlight(TaikoPlayfield taikoPlayfield) public TaikoFlashlight(TaikoPlayfield taikoPlayfield)
{ {
this.taikoPlayfield = taikoPlayfield; this.taikoPlayfield = taikoPlayfield;
FlashlightSize = new Vector2(0, getSizeFor(0)); FlashlightSize = new Vector2(0, getSizeFor(0));
AddLayout(flashlightProperties);
} }
private float getSizeFor(int combo) private float getSizeFor(int combo)
@ -56,16 +58,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
protected override string FragmentShader => "CircularFlashlight"; protected override string FragmentShader => "CircularFlashlight";
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) > 0)
{
flashlightProperties.Invalidate();
}
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Taiko.Objects namespace osu.Game.Rulesets.Taiko.Objects
@ -8,5 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
public class BarLine : TaikoHitObject, IBarLine public class BarLine : TaikoHitObject, IBarLine
{ {
public bool Major { get; set; } public bool Major { get; set; }
public override Judgement CreateJudgement() => new IgnoreJudgement();
} }
} }

View File

@ -54,5 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Alpha = 0.75f Alpha = 0.75f
}); });
} }
protected override void UpdateStateTransforms(ArmedState state) => this.FadeOut(150);
} }
} }

View File

@ -3,13 +3,12 @@
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects namespace osu.Game.Rulesets.Taiko.Objects
{ {
public class SwellTick : TaikoHitObject public class SwellTick : TaikoHitObject
{ {
public override Judgement CreateJudgement() => new TaikoSwellTickJudgement(); public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty; protected override HitWindows CreateHitWindows() => HitWindows.Empty;
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
@ -39,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
{ {
base.ApplyBeatmap(beatmap); base.ApplyBeatmap(beatmap);
hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.OfType<Hit>().Count() * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType<Hit>().Count()) * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
} }

View File

@ -99,7 +99,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var storyboard = decoder.Decode(stream); var storyboard = decoder.Decode(stream);
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3); StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
Assert.AreEqual(123456, ((StoryboardSprite)background.Elements.Single()).InitialPosition.X); Assert.AreEqual(3456, ((StoryboardSprite)background.Elements.Single()).InitialPosition.X);
} }
} }
} }

View File

@ -154,6 +154,7 @@ namespace osu.Game.Tests.Gameplay
private class JudgeableHitObject : HitObject private class JudgeableHitObject : HitObject
{ {
public override Judgement CreateJudgement() => new Judgement(); public override Judgement CreateJudgement() => new Judgement();
protected override HitWindows CreateHitWindows() => new HitWindows();
} }
} }
} }

View File

@ -11,8 +11,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
@ -78,7 +78,7 @@ namespace osu.Game.Tests.Gameplay
} }
} }
private class TestHitObjectWithCombo : HitObject, IHasComboInformation private class TestHitObjectWithCombo : ConvertHitObject, IHasComboInformation
{ {
public bool NewCombo { get; } = false; public bool NewCombo { get; } = false;
public int ComboOffset { get; } = 0; public int ComboOffset { get; } = 0;

View File

@ -1,5 +1,5 @@
[Variables] [Variables]
$var=1234 $var=34
[Events] [Events]
Sprite,Background,TopCentre,"img.jpg",$var56,240 Sprite,Background,TopCentre,"img.jpg",$var56,240

View File

@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Background
private DummySongSelect songSelect; private DummySongSelect songSelect;
private TestPlayerLoader playerLoader; private TestPlayerLoader playerLoader;
private TestPlayer player; private LoadBlockingTestPlayer player;
private BeatmapManager manager; private BeatmapManager manager;
private RulesetStore rulesets; private RulesetStore rulesets;
@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Background
public void PlayerLoaderSettingsHoverTest() public void PlayerLoaderSettingsHoverTest()
{ {
setupUserSettings(); setupUserSettings();
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer { BlockLoad = true }))); AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new LoadBlockingTestPlayer { BlockLoad = true })));
AddUntilStep("Wait for Player Loader to load", () => playerLoader?.IsLoaded ?? false); AddUntilStep("Wait for Player Loader to load", () => playerLoader?.IsLoaded ?? false);
AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent()); AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
AddStep("Trigger background preview", () => AddStep("Trigger background preview", () =>
@ -268,7 +268,7 @@ namespace osu.Game.Tests.Visual.Background
{ {
setupUserSettings(); setupUserSettings();
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer(allowPause)))); AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new LoadBlockingTestPlayer(allowPause))));
AddUntilStep("Wait for Player Loader to load", () => playerLoader.IsLoaded); AddUntilStep("Wait for Player Loader to load", () => playerLoader.IsLoaded);
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos)); AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
@ -347,7 +347,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
} }
private class TestPlayer : Visual.TestPlayer private class LoadBlockingTestPlayer : TestPlayer
{ {
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
@ -360,7 +360,7 @@ namespace osu.Game.Tests.Visual.Background
public readonly Bindable<bool> ReplacesBackground = new Bindable<bool>(); public readonly Bindable<bool> ReplacesBackground = new Bindable<bool>();
public readonly Bindable<bool> IsPaused = new Bindable<bool>(); public readonly Bindable<bool> IsPaused = new Bindable<bool>();
public TestPlayer(bool allowPause = true) public LoadBlockingTestPlayer(bool allowPause = true)
: base(allowPause) : base(allowPause)
{ {
} }

View File

@ -3,53 +3,32 @@
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Storyboards;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[Description("Player instantiated with an autoplay mod.")] [Description("Player instantiated with an autoplay mod.")]
public class TestSceneAutoplay : TestSceneAllRulesetPlayers public class TestSceneAutoplay : TestSceneAllRulesetPlayers
{ {
private ClockBackedTestWorkingBeatmap.TrackVirtualManual track; protected new TestPlayer Player => (TestPlayer)base.Player;
protected override Player CreatePlayer(Ruleset ruleset) protected override Player CreatePlayer(Ruleset ruleset)
{ {
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new ScoreAccessiblePlayer(); return new TestPlayer(false, false);
} }
protected override void AddCheckSteps() protected override void AddCheckSteps()
{ {
AddUntilStep("score above zero", () => ((ScoreAccessiblePlayer)Player).ScoreProcessor.TotalScore.Value > 0); AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2)); AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
AddStep("rewind", () => track.Seek(-10000)); AddStep("seek to break time", () => Player.GameplayClockContainer.Seek(Player.BreakOverlay.Breaks.First().StartTime));
AddUntilStep("key counter reset", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); AddUntilStep("wait for seek to complete", () =>
} Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= Player.BreakOverlay.Breaks.First().StartTime);
AddAssert("test keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
{ AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
var working = base.CreateWorkingBeatmap(beatmap, storyboard);
track = (ClockBackedTestWorkingBeatmap.TrackVirtualManual)working.Track;
return working;
}
private class ScoreAccessiblePlayer : TestPlayer
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public ScoreAccessiblePlayer()
: base(false, false)
{
}
} }
} }
} }

View File

@ -20,6 +20,7 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
@ -289,7 +290,7 @@ namespace osu.Game.Tests.Visual.Gameplay
#region HitObject #region HitObject
private class TestHitObject : HitObject, IHasEndTime private class TestHitObject : ConvertHitObject, IHasEndTime
{ {
public double EndTime { get; set; } public double EndTime { get; set; }

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -11,12 +10,8 @@ using osu.Framework.Utils;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using osu.Game.Storyboards; using osu.Game.Storyboards;
using osuTK; using osuTK;
@ -24,8 +19,6 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestSceneGameplayRewinding : PlayerTestScene public class TestSceneGameplayRewinding : PlayerTestScene
{ {
private RulesetExposingPlayer player => (RulesetExposingPlayer)Player;
[Resolved] [Resolved]
private AudioManager audioManager { get; set; } private AudioManager audioManager { get; set; }
@ -48,13 +41,13 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
AddUntilStep("wait for track to start running", () => track.IsRunning); AddUntilStep("wait for track to start running", () => track.IsRunning);
addSeekStep(3000); addSeekStep(3000);
AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged)); AddAssert("all judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
AddUntilStep("key counter counted keys", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7)); AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7));
AddStep("clear results", () => player.AppliedResults.Clear()); AddStep("clear results", () => Player.Results.Clear());
addSeekStep(0); addSeekStep(0);
AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged)); AddAssert("none judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
AddUntilStep("key counters reset", () => player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); AddUntilStep("key counters reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
AddAssert("no results triggered", () => player.AppliedResults.Count == 0); AddAssert("no results triggered", () => Player.Results.Count == 0);
} }
private void addSeekStep(double time) private void addSeekStep(double time)
@ -62,13 +55,13 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep($"seek to {time}", () => track.Seek(time)); AddStep($"seek to {time}", () => track.Seek(time));
// Allow a few frames of lenience // Allow a few frames of lenience
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100)); AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
} }
protected override Player CreatePlayer(Ruleset ruleset) protected override TestPlayer CreatePlayer(Ruleset ruleset)
{ {
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new RulesetExposingPlayer(); return base.CreatePlayer(ruleset);
} }
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
@ -89,29 +82,5 @@ namespace osu.Game.Tests.Visual.Gameplay
return beatmap; return beatmap;
} }
private class RulesetExposingPlayer : Player
{
public readonly List<JudgementResult> AppliedResults = new List<JudgementResult>();
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
public RulesetExposingPlayer()
: base(false, false)
{
}
[BackgroundDependencyLoader]
private void load()
{
ScoreProcessor.NewJudgement += r => AppliedResults.Add(r);
}
}
} }
} }

View File

@ -2,16 +2,17 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Rulesets.Objects;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Rulesets.Taiko.Scoring;
@ -43,6 +44,22 @@ namespace osu.Game.Tests.Visual.Gameplay
AddRepeatStep("New max negative", () => newJudgement(-hitWindows.WindowFor(HitResult.Meh)), 20); AddRepeatStep("New max negative", () => newJudgement(-hitWindows.WindowFor(HitResult.Meh)), 20);
AddRepeatStep("New max positive", () => newJudgement(hitWindows.WindowFor(HitResult.Meh)), 20); AddRepeatStep("New max positive", () => newJudgement(hitWindows.WindowFor(HitResult.Meh)), 20);
AddStep("New fixed judgement (50ms)", () => newJudgement(50)); AddStep("New fixed judgement (50ms)", () => newJudgement(50));
AddStep("Judgement barrage", () =>
{
int runCount = 0;
ScheduledDelegate del = null;
del = Scheduler.AddDelayed(() =>
{
newJudgement(runCount++ / 10f);
if (runCount == 500)
// ReSharper disable once AccessToModifiedClosure
del?.Cancel();
}, 10, true);
});
} }
[Test] [Test]

View File

@ -47,21 +47,22 @@ namespace osu.Game.Tests.Visual.Gameplay
Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key; Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key;
void addPressKeyStep()
{
AddStep($"Press {testKey} key", () => AddStep($"Press {testKey} key", () =>
{ {
InputManager.PressKey(testKey); InputManager.PressKey(testKey);
InputManager.ReleaseKey(testKey); InputManager.ReleaseKey(testKey);
}); });
}
addPressKeyStep();
AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 1); AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 1);
addPressKeyStep();
AddStep($"Press {testKey} key", () =>
{
InputManager.PressKey(testKey);
InputManager.ReleaseKey(testKey);
});
AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 2); AddAssert($"Check {testKey} counter after keypress", () => testCounter.CountPresses == 2);
AddStep("Disable counting", () => testCounter.IsCounting = false);
addPressKeyStep();
AddAssert($"Check {testKey} count has not changed", () => testCounter.CountPresses == 2);
Add(kc); Add(kc);
} }

View File

@ -11,7 +11,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osuTK; using osuTK;
using osuTK.Input; using osuTK.Input;
@ -282,14 +281,10 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override bool AllowFail => true; protected override bool AllowFail => true;
protected override Player CreatePlayer(Ruleset ruleset) => new PausePlayer(); protected override TestPlayer CreatePlayer(Ruleset ruleset) => new PausePlayer();
protected class PausePlayer : TestPlayer protected class PausePlayer : TestPlayer
{ {
public new HealthProcessor HealthProcessor => base.HealthProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public bool FailOverlayVisible => FailOverlay.State.Value == Visibility.Visible; public bool FailOverlayVisible => FailOverlay.State.Value == Visibility.Visible;
public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible; public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible;

View File

@ -9,15 +9,12 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[HeadlessTest] // we alter unsafe properties on the game host to test inactive window state. [HeadlessTest] // we alter unsafe properties on the game host to test inactive window state.
public class TestScenePauseWhenInactive : PlayerTestScene public class TestScenePauseWhenInactive : PlayerTestScene
{ {
protected new TestPlayer Player => (TestPlayer)base.Player;
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{ {
var beatmap = (Beatmap)base.CreateBeatmap(ruleset); var beatmap = (Beatmap)base.CreateBeatmap(ruleset);
@ -46,6 +43,6 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("time of pause is after gameplay start time", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= Player.DrawableRuleset.GameplayStartTime); AddAssert("time of pause is after gameplay start time", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= Player.DrawableRuleset.GameplayStartTime);
} }
protected override Player CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true); protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true);
} }
} }

View File

@ -9,7 +9,6 @@ using System.Threading.Tasks;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Utils; using osu.Framework.Utils;
@ -307,17 +306,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
} }
private class TestPlayer : Visual.TestPlayer protected class SlowLoadPlayer : TestPlayer
{
public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods;
public TestPlayer(bool allowPause = true, bool showResults = true)
: base(allowPause, showResults)
{
}
}
protected class SlowLoadPlayer : Visual.TestPlayer
{ {
public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim(false); public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim(false);

View File

@ -3,9 +3,6 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
using osuTK; using osuTK;
@ -45,32 +42,12 @@ namespace osu.Game.Tests.Visual.Gameplay
}; };
Add(accuracyCounter); Add(accuracyCounter);
StarCounter stars = new StarCounter
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Position = new Vector2(20, -160),
CountStars = 5,
};
Add(stars);
SpriteText starsLabel = new OsuSpriteText
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Position = new Vector2(20, -190),
Text = stars.CountStars.ToString("0.00"),
};
Add(starsLabel);
AddStep(@"Reset all", delegate AddStep(@"Reset all", delegate
{ {
score.Current.Value = 0; score.Current.Value = 0;
comboCounter.Current.Value = 0; comboCounter.Current.Value = 0;
numerator = denominator = 0; numerator = denominator = 0;
accuracyCounter.SetFraction(0, 0); accuracyCounter.SetFraction(0, 0);
stars.CountStars = 0;
starsLabel.Text = stars.CountStars.ToString("0.00");
}); });
AddStep(@"Hit! :D", delegate AddStep(@"Hit! :D", delegate
@ -88,20 +65,6 @@ namespace osu.Game.Tests.Visual.Gameplay
denominator++; denominator++;
accuracyCounter.SetFraction(numerator, denominator); accuracyCounter.SetFraction(numerator, denominator);
}); });
AddStep(@"Alter stars", delegate
{
stars.CountStars = RNG.NextSingle() * (stars.StarCount + 1);
starsLabel.Text = stars.CountStars.ToString("0.00");
});
AddStep(@"Stop counters", delegate
{
score.StopRolling();
comboCounter.StopRolling();
accuracyCounter.StopRolling();
stars.StopAnimation();
});
} }
} }
} }

View File

@ -0,0 +1,57 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
namespace osu.Game.Tests.Visual.Gameplay
{
[TestFixture]
public class TestSceneStarCounter : OsuTestScene
{
public TestSceneStarCounter()
{
StarCounter stars = new StarCounter
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Current = 5,
};
Add(stars);
SpriteText starsLabel = new OsuSpriteText
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Scale = new Vector2(2),
Y = 50,
Text = stars.Current.ToString("0.00"),
};
Add(starsLabel);
AddRepeatStep(@"random value", delegate
{
stars.Current = RNG.NextSingle() * (stars.StarCount + 1);
starsLabel.Text = stars.Current.ToString("0.00");
}, 10);
AddStep(@"Stop animation", delegate
{
stars.StopAnimation();
});
AddStep(@"Reset", delegate
{
stars.Current = 0;
starsLabel.Text = stars.Current.ToString("0.00");
});
}
}
}

View File

@ -62,6 +62,17 @@ namespace osu.Game.Tests.Visual.Navigation
var frameworkConfig = host.Dependencies.Get<FrameworkConfigManager>(); var frameworkConfig = host.Dependencies.Get<FrameworkConfigManager>();
frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity).Disabled = false; frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity).Disabled = false;
CreateGame();
});
AddUntilStep("Wait for load", () => Game.IsLoaded);
AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroScreen);
ConfirmAtMainMenu();
}
protected void CreateGame()
{
Game = new TestOsuGame(LocalStorage, API); Game = new TestOsuGame(LocalStorage, API);
Game.SetHost(host); Game.SetHost(host);
@ -70,12 +81,6 @@ namespace osu.Game.Tests.Visual.Navigation
Game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles); Game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
Add(Game); Add(Game);
});
AddUntilStep("Wait for load", () => Game.IsLoaded);
AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroScreen);
ConfirmAtMainMenu();
} }
protected void PushAndConfirm(Func<Screen> newScreen) protected void PushAndConfirm(Func<Screen> newScreen)
@ -97,12 +102,17 @@ namespace osu.Game.Tests.Visual.Navigation
public new SettingsPanel Settings => base.Settings; public new SettingsPanel Settings => base.Settings;
public new MusicController MusicController => base.MusicController;
public new OsuConfigManager LocalConfig => base.LocalConfig; public new OsuConfigManager LocalConfig => base.LocalConfig;
public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap; public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap;
public new Bindable<RulesetInfo> Ruleset => base.Ruleset; public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
// if we don't do this, when running under nUnit the version that gets populated is that of nUnit.
public override string Version => "test game";
protected override Loader CreateLoader() => new TestLoader(); protected override Loader CreateLoader() => new TestLoader();
public new void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null) => base.PerformFromScreen(action, validScreens); public new void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null) => base.PerformFromScreen(action, validScreens);

View File

@ -114,6 +114,22 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("Options overlay was closed", () => Game.Settings.State.Value == Visibility.Hidden); AddAssert("Options overlay was closed", () => Game.Settings.State.Value == Visibility.Hidden);
} }
[Test]
public void TestWaitForNextTrackInMenu()
{
bool trackCompleted = false;
AddUntilStep("Wait for music controller", () => Game.MusicController.IsLoaded);
AddStep("Seek close to end", () =>
{
Game.MusicController.SeekTo(Game.Beatmap.Value.Track.Length - 1000);
Game.Beatmap.Value.Track.Completed += () => trackCompleted = true;
});
AddUntilStep("Track was completed", () => trackCompleted);
AddUntilStep("Track was restarted", () => Game.Beatmap.Value.Track.IsRunning);
}
private void pushEscape() => private void pushEscape() =>
AddStep("Press escape", () => pressAndRelease(Key.Escape)); AddStep("Press escape", () => pressAndRelease(Key.Escape));

View File

@ -0,0 +1,41 @@
// 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 NUnit.Framework;
using osu.Framework.Utils;
using osu.Game.Configuration;
namespace osu.Game.Tests.Visual.Navigation
{
public class TestSettingsMigration : OsuGameTestScene
{
public override void RecycleLocalStorage()
{
base.RecycleLocalStorage();
using (var config = new OsuConfigManager(LocalStorage))
{
config.Set(OsuSetting.Version, "2020.101.0");
config.Set(OsuSetting.DisplayStarsMaximum, 10.0);
}
}
[Test]
public void TestDisplayStarsMigration()
{
AddAssert("config has migrated value", () => Precision.AlmostEquals(Game.LocalConfig.Get<double>(OsuSetting.DisplayStarsMaximum), 10.1));
AddStep("set value again", () => Game.LocalConfig.Set<double>(OsuSetting.DisplayStarsMaximum, 10));
AddStep("force save config", () => Game.LocalConfig.Save());
AddStep("remove game", () => Remove(Game));
AddStep("create game again", CreateGame);
AddUntilStep("Wait for load", () => Game.IsLoaded);
AddAssert("config did not migrate value", () => Precision.AlmostEquals(Game.LocalConfig.Get<double>(OsuSetting.DisplayStarsMaximum), 10));
}
}
}

View File

@ -13,6 +13,8 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osuTK; using osuTK;
using JetBrains.Annotations;
using NUnit.Framework;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
@ -30,6 +32,8 @@ namespace osu.Game.Tests.Visual.Online
private readonly BindableBool showDeleted = new BindableBool(); private readonly BindableBool showDeleted = new BindableBool();
private readonly Container content; private readonly Container content;
private TestCommentsPage commentsPage;
public TestSceneCommentsPage() public TestSceneCommentsPage()
{ {
Add(new FillFlowContainer Add(new FillFlowContainer
@ -57,15 +61,29 @@ namespace osu.Game.Tests.Visual.Online
} }
} }
}); });
}
AddStep("load comments", () => createPage(getCommentBundle())); [Test]
AddStep("load empty comments", () => createPage(getEmptyCommentBundle())); public void TestAppendDuplicatedComment()
{
AddStep("Create page", () => createPage(getCommentBundle()));
AddAssert("Dictionary length is 10", () => commentsPage?.DictionaryLength == 10);
AddStep("Append existing comment", () => commentsPage?.AppendComments(getCommentSubBundle()));
AddAssert("Dictionary length is 10", () => commentsPage?.DictionaryLength == 10);
}
[Test]
public void TestEmptyBundle()
{
AddStep("Create page", () => createPage(getEmptyCommentBundle()));
AddAssert("Dictionary length is 0", () => commentsPage?.DictionaryLength == 0);
} }
private void createPage(CommentBundle commentBundle) private void createPage(CommentBundle commentBundle)
{ {
commentsPage = null;
content.Clear(); content.Clear();
content.Add(new CommentsPage(commentBundle) content.Add(commentsPage = new TestCommentsPage(commentBundle)
{ {
ShowDeleted = { BindTarget = showDeleted } ShowDeleted = { BindTarget = showDeleted }
}); });
@ -182,5 +200,33 @@ namespace osu.Game.Tests.Visual.Online
} }
}, },
}; };
private CommentBundle getCommentSubBundle() => new CommentBundle
{
Comments = new List<Comment>
{
new Comment
{
Id = 1,
Message = "Simple test comment",
LegacyName = "TestUser1",
CreatedAt = DateTimeOffset.Now,
VotesCount = 5
},
},
IncludedComments = new List<Comment>(),
};
private class TestCommentsPage : CommentsPage
{
public TestCommentsPage(CommentBundle commentBundle)
: base(commentBundle)
{
}
public new void AppendComments([NotNull] CommentBundle bundle) => base.AppendComments(bundle);
public int DictionaryLength => CommentDictionary.Count;
}
} }
} }

View File

@ -2,9 +2,11 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
@ -13,13 +15,19 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture] [TestFixture]
public class TestSceneUserPanel : OsuTestScene public class TestSceneUserPanel : OsuTestScene
{ {
private readonly UserPanel peppy; private readonly Bindable<UserActivity> activity = new Bindable<UserActivity>();
public TestSceneUserPanel() private UserPanel peppy;
[Resolved]
private RulesetStore rulesetStore { get; set; }
[SetUp]
public void SetUp() => Schedule(() =>
{ {
UserPanel flyte; UserPanel flyte;
Add(new FillFlowContainer Child = new FillFlowContainer
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -44,34 +52,38 @@ namespace osu.Game.Tests.Visual.Online
SupportLevel = 3, SupportLevel = 3,
}) { Width = 300 }, }) { Width = 300 },
}, },
}); };
flyte.Status.Value = new UserStatusOnline(); flyte.Status.Value = new UserStatusOnline();
peppy.Status.Value = null; peppy.Status.Value = null;
}
[Test]
public void UserStatusesTests()
{
AddStep("online", () => { peppy.Status.Value = new UserStatusOnline(); });
AddStep(@"do not disturb", () => { peppy.Status.Value = new UserStatusDoNotDisturb(); });
AddStep(@"offline", () => { peppy.Status.Value = new UserStatusOffline(); });
AddStep(@"null status", () => { peppy.Status.Value = null; });
}
[Test]
public void UserActivitiesTests()
{
Bindable<UserActivity> activity = new Bindable<UserActivity>();
peppy.Activity.BindTo(activity); peppy.Activity.BindTo(activity);
});
AddStep("idle", () => { activity.Value = null; }); [Test]
AddStep("spectating", () => { activity.Value = new UserActivity.Spectating(); }); public void TestUserStatus()
AddStep("solo", () => { activity.Value = new UserActivity.SoloGame(null, null); }); {
AddStep("choosing", () => { activity.Value = new UserActivity.ChoosingBeatmap(); }); AddStep("online", () => peppy.Status.Value = new UserStatusOnline());
AddStep("editing", () => { activity.Value = new UserActivity.Editing(null); }); AddStep("do not disturb", () => peppy.Status.Value = new UserStatusDoNotDisturb());
AddStep("modding", () => { activity.Value = new UserActivity.Modding(); }); AddStep("offline", () => peppy.Status.Value = new UserStatusOffline());
} AddStep("null status", () => peppy.Status.Value = null);
}
[Test]
public void TestUserActivity()
{
AddStep("set online status", () => peppy.Status.Value = new UserStatusOnline());
AddStep("idle", () => activity.Value = null);
AddStep("spectating", () => activity.Value = new UserActivity.Spectating());
AddStep("solo (osu!)", () => activity.Value = soloGameStatusForRuleset(0));
AddStep("solo (osu!taiko)", () => activity.Value = soloGameStatusForRuleset(1));
AddStep("solo (osu!catch)", () => activity.Value = soloGameStatusForRuleset(2));
AddStep("solo (osu!mania)", () => activity.Value = soloGameStatusForRuleset(3));
AddStep("choosing", () => activity.Value = new UserActivity.ChoosingBeatmap());
AddStep("editing", () => activity.Value = new UserActivity.Editing(null));
AddStep("modding", () => activity.Value = new UserActivity.Modding());
}
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.SoloGame(null, rulesetStore.GetRuleset(rulesetId));
} }
} }

View File

@ -7,6 +7,8 @@ using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Overlays.Comments; using osu.Game.Overlays.Comments;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Framework.Allocation;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
@ -18,6 +20,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(VotePill) typeof(VotePill)
}; };
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private VotePill votePill; private VotePill votePill;
[Test] [Test]

View File

@ -13,6 +13,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Taiko;
@ -194,7 +195,7 @@ namespace osu.Game.Tests.Visual.SongSelect
public new BufferedWedgeInfo Info => base.Info; public new BufferedWedgeInfo Info => base.Info;
} }
private class TestHitObject : HitObject, IHasPosition private class TestHitObject : ConvertHitObject, IHasPosition
{ {
public float X { get; } = 0; public float X { get; } = 0;
public float Y { get; } = 0; public float Y { get; } = 0;

View File

@ -0,0 +1,57 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Home.Friends;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneUserListToolbar : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(UserSortTabControl),
typeof(OverlaySortTabControl<>),
typeof(OverlayPanelDisplayStyleControl),
typeof(UserListToolbar),
};
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
public TestSceneUserListToolbar()
{
UserListToolbar toolbar;
OsuSpriteText sort;
OsuSpriteText displayStyle;
Add(toolbar = new UserListToolbar
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
Add(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
sort = new OsuSpriteText(),
displayStyle = new OsuSpriteText()
}
});
toolbar.SortCriteria.BindValueChanged(criteria => sort.Text = $"Criteria: {criteria.NewValue}", true);
toolbar.DisplayStyle.BindValueChanged(style => displayStyle.Text = $"Style: {style.NewValue}", true);
}
}
}

View File

@ -0,0 +1,125 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Tests.Visual;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Drawings.Components;
using osu.Game.Tournament.Screens.Gameplay.Components;
using osu.Game.Tournament.Screens.Ladder.Components;
using osu.Game.Users;
namespace osu.Game.Tournament.Tests.Components
{
public class TestSceneDrawableTournamentTeam : OsuGridTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(DrawableTeamFlag),
typeof(DrawableTeamTitle),
typeof(DrawableTeamTitleWithHeader),
typeof(DrawableMatchTeam),
typeof(DrawableTeamWithPlayers),
typeof(GroupTeam),
typeof(TeamDisplay),
};
public TestSceneDrawableTournamentTeam()
: base(4, 3)
{
var team = new TournamentTeam
{
FlagName = { Value = "AU" },
FullName = { Value = "Australia" },
Players =
{
new User { Username = "ASecretBox" },
new User { Username = "Dereban" },
new User { Username = "mReKk" },
new User { Username = "uyghti" },
new User { Username = "Parkes" },
new User { Username = "Shiroha" },
new User { Username = "Jordan The Bear" },
}
};
var match = new TournamentMatch { Team1 = { Value = team } };
int i = 0;
Cell(i++).AddRange(new Drawable[]
{
new TournamentSpriteText { Text = "DrawableTeamFlag" },
new DrawableTeamFlag(team)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
Cell(i++).AddRange(new Drawable[]
{
new TournamentSpriteText { Text = "DrawableTeamTitle" },
new DrawableTeamTitle(team)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
Cell(i++).AddRange(new Drawable[]
{
new TournamentSpriteText { Text = "DrawableTeamTitleWithHeader" },
new DrawableTeamTitleWithHeader(team, TeamColour.Red)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
Cell(i++).AddRange(new Drawable[]
{
new TournamentSpriteText { Text = "DrawableMatchTeam" },
new DrawableMatchTeam(team, match, false)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
Cell(i++).AddRange(new Drawable[]
{
new TournamentSpriteText { Text = "TeamWithPlayers" },
new DrawableTeamWithPlayers(team, TeamColour.Blue)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
Cell(i++).AddRange(new Drawable[]
{
new TournamentSpriteText { Text = "GroupTeam" },
new GroupTeam(team)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
Cell(i).AddRange(new Drawable[]
{
new TournamentSpriteText { Text = "TeamDisplay" },
new TeamDisplay(team, TeamColour.Red, new Bindable<int?>(2), 6)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
}
}
}

View File

@ -1,16 +1,31 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.Screens;
using osu.Game.Tournament.Screens.Gameplay; using osu.Game.Tournament.Screens.Gameplay;
using osu.Game.Tournament.Screens.Gameplay.Components;
namespace osu.Game.Tournament.Tests.Screens namespace osu.Game.Tournament.Tests.Screens
{ {
public class TestSceneGameplayScreen : TournamentTestScene public class TestSceneGameplayScreen : TournamentTestScene
{ {
[Cached] [Cached]
private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay(); private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay { Width = 0.5f };
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(TeamScore),
typeof(TeamScoreDisplay),
typeof(TeamDisplay),
typeof(MatchHeader),
typeof(MatchScoreDisplay),
typeof(BeatmapInfoScreen),
typeof(SongBar),
};
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()

View File

@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Screens.Schedule; using osu.Game.Tournament.Screens.Schedule;
namespace osu.Game.Tournament.Tests.Screens namespace osu.Game.Tournament.Tests.Screens
@ -11,6 +13,7 @@ namespace osu.Game.Tournament.Tests.Screens
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Add(new TourneyVideo("main") { RelativeSizeAxes = Axes.Both });
Add(new ScheduleScreen()); Add(new ScheduleScreen());
} }
} }

View File

@ -0,0 +1,25 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Editors;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneSeedingEditorScreen : LadderTestScene
{
[Cached]
private readonly LadderInfo ladder = new LadderInfo();
public TestSceneSeedingEditorScreen()
{
var match = TestSceneSeedingScreen.CreateSampleSeededMatch();
Add(new SeedingEditorScreen(match.Team1.Value)
{
Width = 0.85f // create room for control panel
});
}
}
}

View File

@ -0,0 +1,127 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.TeamIntro;
using osu.Game.Users;
namespace osu.Game.Tournament.Tests.Screens
{
public class TestSceneSeedingScreen : LadderTestScene
{
[Cached]
private readonly LadderInfo ladder = new LadderInfo();
[BackgroundDependencyLoader]
private void load()
{
ladder.CurrentMatch.Value = CreateSampleSeededMatch();
Add(new SeedingScreen
{
FillMode = FillMode.Fit,
FillAspectRatio = 16 / 9f
});
}
public static TournamentMatch CreateSampleSeededMatch() => new TournamentMatch
{
Team1 =
{
Value = new TournamentTeam
{
FlagName = { Value = "JP" },
FullName = { Value = "Japan" },
LastYearPlacing = { Value = 10 },
Seed = { Value = "Low" },
SeedingResults =
{
new SeedingResult
{
Mod = { Value = "NM" },
Seed = { Value = 10 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 12345672,
Seed = { Value = 24 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 1234567,
Seed = { Value = 12 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 1234567,
Seed = { Value = 16 },
}
}
},
new SeedingResult
{
Mod = { Value = "DT" },
Seed = { Value = 5 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 234567,
Seed = { Value = 3 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 234567,
Seed = { Value = 6 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 234567,
Seed = { Value = 12 },
}
}
}
},
Players =
{
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 12 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 16 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 20 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 30 } } },
}
}
},
Team2 =
{
Value = new TournamentTeam
{
FlagName = { Value = "US" },
FullName = { Value = "United States" },
Players =
{
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
}
}
},
Round =
{
Value = new TournamentRound { Name = { Value = "Quarterfinals" } }
}
};
}
}

View File

@ -21,6 +21,7 @@ namespace osu.Game.Tournament.Tests.Screens
match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA"); match.Team1.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "USA");
match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN"); match.Team2.Value = Ladder.Teams.FirstOrDefault(t => t.Acronym.Value == "JPN");
match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals"); match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals");
match.Completed.Value = true;
ladder.CurrentMatch.Value = match; ladder.CurrentMatch.Value = match;
Add(new TeamWinScreen Add(new TeamWinScreen

View File

@ -5,7 +5,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -35,7 +34,7 @@ namespace osu.Game.Tournament.Components
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = new Color4(54, 54, 54, 255) Colour = new Color4(54, 54, 54, 255)
}, },
new OsuSpriteText new TournamentSpriteText
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,

View File

@ -0,0 +1,33 @@
// 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 JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Tournament.Models;
namespace osu.Game.Tournament.Components
{
public class DrawableTeamFlag : Sprite
{
private readonly TournamentTeam team;
[UsedImplicitly]
private Bindable<string> flag;
public DrawableTeamFlag(TournamentTeam team)
{
this.team = team;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
if (team == null) return;
(flag = team.FlagName.GetBoundCopy()).BindValueChanged(acronym => Texture = textures.Get($@"Flags/{team.FlagName}"), true);
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Tournament.Models;
using osuTK;
namespace osu.Game.Tournament.Components
{
public class DrawableTeamHeader : TournamentSpriteTextWithBackground
{
public DrawableTeamHeader(TeamColour colour)
{
Background.Colour = TournamentGame.GetTeamColour(colour);
Text.Colour = TournamentGame.TEXT_COLOUR;
Text.Text = $"Team {colour}".ToUpperInvariant();
Text.Scale = new Vector2(0.6f);
}
}
}

View File

@ -0,0 +1,32 @@
// 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 JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Textures;
using osu.Game.Tournament.Models;
namespace osu.Game.Tournament.Components
{
public class DrawableTeamTitle : TournamentSpriteTextWithBackground
{
private readonly TournamentTeam team;
[UsedImplicitly]
private Bindable<string> acronym;
public DrawableTeamTitle(TournamentTeam team)
{
this.team = team;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
if (team == null) return;
(acronym = team.Acronym.GetBoundCopy()).BindValueChanged(acronym => Text.Text = team?.FullName.Value ?? string.Empty, true);
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Tournament.Models;
using osuTK;
namespace osu.Game.Tournament.Components
{
public class DrawableTeamTitleWithHeader : CompositeDrawable
{
public DrawableTeamTitleWithHeader(TournamentTeam team, TeamColour colour)
{
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
new DrawableTeamHeader(colour),
new DrawableTeamTitle(team),
}
};
}
}
}

View File

@ -0,0 +1,66 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Tournament.Models;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tournament.Components
{
public class DrawableTeamWithPlayers : CompositeDrawable
{
public DrawableTeamWithPlayers(TournamentTeam team, TeamColour colour)
{
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(30),
Children = new Drawable[]
{
new DrawableTeamTitleWithHeader(team, colour),
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Left = 10 },
Spacing = new Vector2(30),
Children = new Drawable[]
{
new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
ChildrenEnumerable = team?.Players.Select(createPlayerText).Take(5) ?? Enumerable.Empty<Drawable>()
},
new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
ChildrenEnumerable = team?.Players.Select(createPlayerText).Skip(5) ?? Enumerable.Empty<Drawable>()
},
}
},
}
},
};
TournamentSpriteText createPlayerText(User p) =>
new TournamentSpriteText
{
Text = p.Username,
Font = OsuFont.Torus.With(size: 24, weight: FontWeight.SemiBold),
Colour = Color4.White,
};
}
}
}

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
namespace osu.Game.Tournament.Components namespace osu.Game.Tournament.Components
@ -19,27 +18,24 @@ namespace osu.Game.Tournament.Components
public readonly TournamentTeam Team; public readonly TournamentTeam Team;
protected readonly Sprite Flag; protected readonly Sprite Flag;
protected readonly OsuSpriteText AcronymText; protected readonly TournamentSpriteText AcronymText;
[UsedImplicitly] [UsedImplicitly]
private Bindable<string> acronym; private Bindable<string> acronym;
[UsedImplicitly]
private Bindable<string> flag;
protected DrawableTournamentTeam(TournamentTeam team) protected DrawableTournamentTeam(TournamentTeam team)
{ {
Team = team; Team = team;
Flag = new Sprite Flag = new DrawableTeamFlag(team)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit FillMode = FillMode.Fit
}; };
AcronymText = new OsuSpriteText AcronymText = new TournamentSpriteText
{ {
Font = OsuFont.GetFont(weight: FontWeight.Regular), Font = OsuFont.Torus.With(weight: FontWeight.Regular),
}; };
} }
@ -49,7 +45,6 @@ namespace osu.Game.Tournament.Components
if (Team == null) return; if (Team == null) return;
(acronym = Team.Acronym.GetBoundCopy()).BindValueChanged(acronym => AcronymText.Text = Team?.Acronym.Value?.ToUpperInvariant() ?? string.Empty, true); (acronym = Team.Acronym.GetBoundCopy()).BindValueChanged(acronym => AcronymText.Text = Team?.Acronym.Value?.ToUpperInvariant() ?? string.Empty, true);
(flag = Team.FlagName.GetBoundCopy()).BindValueChanged(acronym => Flag.Texture = textures.Get($@"Flags/{Team.FlagName}"), true);
} }
} }
} }

View File

@ -0,0 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Graphics;
namespace osu.Game.Tournament.Components
{
public class DrawableTournamentTitleText : TournamentSpriteText
{
public DrawableTournamentTitleText()
{
Text = "osu!taiko world cup 2020";
Font = OsuFont.Torus.With(size: 26, weight: FontWeight.SemiBold);
}
}
}

Some files were not shown because too many files have changed in this diff Show More