mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 16:52:54 +08:00
Merge github.com:ppy/osu into patch/wiki-locale
This commit is contained in:
commit
64468bce6d
@ -21,7 +21,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ppy.localisationanalyser.tools": {
|
"ppy.localisationanalyser.tools": {
|
||||||
"version": "2024.517.0",
|
"version": "2024.802.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"localisation"
|
"localisation"
|
||||||
]
|
]
|
||||||
|
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@ -121,9 +121,7 @@ jobs:
|
|||||||
|
|
||||||
build-only-ios:
|
build-only-ios:
|
||||||
name: Build only (iOS)
|
name: Build only (iOS)
|
||||||
# `macos-13` is required, because the newest Microsoft.iOS.Sdk versions require Xcode 14.3.
|
runs-on: macos-latest
|
||||||
# TODO: can be changed to `macos-latest` once `macos-13` becomes latest (currently in beta: https://github.com/actions/runner-images/tree/main#available-images)
|
|
||||||
runs-on: macos-13
|
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@ -137,8 +135,5 @@ jobs:
|
|||||||
- name: Install .NET Workloads
|
- name: Install .NET Workloads
|
||||||
run: dotnet workload install maui-ios
|
run: dotnet workload install maui-ios
|
||||||
|
|
||||||
- name: Select Xcode 15.2
|
|
||||||
run: sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build -c Debug osu.iOS
|
run: dotnet build -c Debug osu.iOS
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.720.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.809.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin)
|
public void TestLegacyHUDComboCounterNotExistent([Values] bool withModifiedSkin)
|
||||||
{
|
{
|
||||||
if (withModifiedSkin)
|
if (withModifiedSkin)
|
||||||
{
|
{
|
||||||
@ -29,10 +28,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
CreateTest();
|
CreateTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
AddAssert("legacy HUD combo counter hidden", () =>
|
AddAssert("legacy HUD combo counter not added", () => !Player.ChildrenOfType<LegacyComboCounter>().Any());
|
||||||
{
|
|
||||||
return Player.ChildrenOfType<LegacyComboCounter>().All(c => c.ChildrenOfType<Container>().Single().Alpha == 0f);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,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.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||||
@ -28,76 +29,93 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
|
|
||||||
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
||||||
{
|
{
|
||||||
if (lookup is SkinComponentsContainerLookup containerLookup)
|
switch (lookup)
|
||||||
{
|
{
|
||||||
switch (containerLookup.Target)
|
case SkinComponentsContainerLookup containerLookup:
|
||||||
{
|
if (containerLookup.Target != SkinComponentsContainerLookup.TargetArea.MainHUDComponents)
|
||||||
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
return base.GetDrawableComponent(lookup);
|
||||||
var components = base.GetDrawableComponent(lookup) as Container;
|
|
||||||
|
|
||||||
if (providesComboCounter && components != null)
|
// Modifications for global components.
|
||||||
|
if (containerLookup.Ruleset == null)
|
||||||
|
return base.GetDrawableComponent(lookup) as Container;
|
||||||
|
|
||||||
|
// Skin has configuration.
|
||||||
|
if (base.GetDrawableComponent(lookup) is UserConfiguredLayoutContainer d)
|
||||||
|
return d;
|
||||||
|
|
||||||
|
// Our own ruleset components default.
|
||||||
|
// todo: remove CatchSkinComponents.CatchComboCounter and refactor LegacyCatchComboCounter to be added here instead.
|
||||||
|
return new DefaultSkinComponentsContainer(container =>
|
||||||
|
{
|
||||||
|
var keyCounter = container.OfType<LegacyKeyCounterDisplay>().FirstOrDefault();
|
||||||
|
|
||||||
|
if (keyCounter != null)
|
||||||
{
|
{
|
||||||
// catch may provide its own combo counter; hide the default.
|
// set the anchor to top right so that it won't squash to the return button to the top
|
||||||
// todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed.
|
keyCounter.Anchor = Anchor.CentreRight;
|
||||||
foreach (var legacyComboCounter in components.OfType<LegacyComboCounter>())
|
keyCounter.Origin = Anchor.CentreRight;
|
||||||
legacyComboCounter.HiddenByRulesetImplementation = false;
|
keyCounter.X = 0;
|
||||||
|
// 340px is the default height inherit from stable
|
||||||
|
keyCounter.Y = container.ToLocalSpace(new Vector2(0, container.ScreenSpaceDrawQuad.Centre.Y - 340f)).Y;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
return components;
|
{
|
||||||
}
|
Children = new Drawable[]
|
||||||
}
|
|
||||||
|
|
||||||
if (lookup is CatchSkinComponentLookup catchSkinComponent)
|
|
||||||
{
|
|
||||||
switch (catchSkinComponent.Component)
|
|
||||||
{
|
|
||||||
case CatchSkinComponents.Fruit:
|
|
||||||
if (hasPear)
|
|
||||||
return new LegacyFruitPiece();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case CatchSkinComponents.Banana:
|
|
||||||
if (GetTexture("fruit-bananas") != null)
|
|
||||||
return new LegacyBananaPiece();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case CatchSkinComponents.Droplet:
|
|
||||||
if (GetTexture("fruit-drop") != null)
|
|
||||||
return new LegacyDropletPiece();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case CatchSkinComponents.Catcher:
|
|
||||||
decimal version = GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value ?? 1;
|
|
||||||
|
|
||||||
if (version < 2.3m)
|
|
||||||
{
|
{
|
||||||
if (hasOldStyleCatcherSprite())
|
new LegacyKeyCounterDisplay(),
|
||||||
return new LegacyCatcherOld();
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (hasNewStyleCatcherSprite())
|
case CatchSkinComponentLookup catchSkinComponent:
|
||||||
return new LegacyCatcherNew();
|
switch (catchSkinComponent.Component)
|
||||||
|
{
|
||||||
|
case CatchSkinComponents.Fruit:
|
||||||
|
if (hasPear)
|
||||||
|
return new LegacyFruitPiece();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case CatchSkinComponents.CatchComboCounter:
|
case CatchSkinComponents.Banana:
|
||||||
if (providesComboCounter)
|
if (GetTexture("fruit-bananas") != null)
|
||||||
return new LegacyCatchComboCounter();
|
return new LegacyBananaPiece();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case CatchSkinComponents.HitExplosion:
|
case CatchSkinComponents.Droplet:
|
||||||
if (hasOldStyleCatcherSprite() || hasNewStyleCatcherSprite())
|
if (GetTexture("fruit-drop") != null)
|
||||||
return new LegacyHitExplosion();
|
return new LegacyDropletPiece();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
default:
|
case CatchSkinComponents.Catcher:
|
||||||
throw new UnsupportedSkinComponentException(lookup);
|
decimal version = GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value ?? 1;
|
||||||
}
|
|
||||||
|
if (version < 2.3m)
|
||||||
|
{
|
||||||
|
if (hasOldStyleCatcherSprite())
|
||||||
|
return new LegacyCatcherOld();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNewStyleCatcherSprite())
|
||||||
|
return new LegacyCatcherNew();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case CatchSkinComponents.CatchComboCounter:
|
||||||
|
if (providesComboCounter)
|
||||||
|
return new LegacyCatchComboCounter();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case CatchSkinComponents.HitExplosion:
|
||||||
|
if (hasOldStyleCatcherSprite() || hasNewStyleCatcherSprite())
|
||||||
|
return new LegacyHitExplosion();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new UnsupportedSkinComponentException(lookup);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.GetDrawableComponent(lookup);
|
return base.GetDrawableComponent(lookup);
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
});
|
});
|
||||||
|
|
||||||
moveMouseToHitObject(1);
|
moveMouseToHitObject(1);
|
||||||
AddAssert("merge option available", () => selectionHandler.ContextMenuItems?.Any(o => o.Text.Value == "Merge selection") == true);
|
AddAssert("merge option available", () => selectionHandler.ContextMenuItems.Any(o => o.Text.Value == "Merge selection"));
|
||||||
|
|
||||||
mergeSelection();
|
mergeSelection();
|
||||||
|
|
||||||
@ -198,7 +198,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
});
|
});
|
||||||
|
|
||||||
moveMouseToHitObject(1);
|
moveMouseToHitObject(1);
|
||||||
AddAssert("merge option not available", () => selectionHandler.ContextMenuItems?.Length > 0 && selectionHandler.ContextMenuItems.All(o => o.Text.Value != "Merge selection"));
|
AddAssert("merge option not available", () => selectionHandler.ContextMenuItems.Length > 0 && selectionHandler.ContextMenuItems.All(o => o.Text.Value != "Merge selection"));
|
||||||
mergeSelection();
|
mergeSelection();
|
||||||
AddAssert("circles not merged", () => circle1 is not null && circle2 is not null
|
AddAssert("circles not merged", () => circle1 is not null && circle2 is not null
|
||||||
&& EditorBeatmap.HitObjects.Contains(circle1) && EditorBeatmap.HitObjects.Contains(circle2));
|
&& EditorBeatmap.HitObjects.Contains(circle1) && EditorBeatmap.HitObjects.Contains(circle2));
|
||||||
@ -222,7 +222,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
});
|
});
|
||||||
|
|
||||||
moveMouseToHitObject(1);
|
moveMouseToHitObject(1);
|
||||||
AddAssert("merge option available", () => selectionHandler.ContextMenuItems?.Any(o => o.Text.Value == "Merge selection") == true);
|
AddAssert("merge option available", () => selectionHandler.ContextMenuItems.Any(o => o.Text.Value == "Merge selection"));
|
||||||
|
|
||||||
mergeSelection();
|
mergeSelection();
|
||||||
|
|
||||||
|
@ -42,7 +42,12 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
{
|
{
|
||||||
base.PostProcess();
|
base.PostProcess();
|
||||||
|
|
||||||
var hitObjects = Beatmap.HitObjects as List<OsuHitObject> ?? Beatmap.HitObjects.OfType<OsuHitObject>().ToList();
|
ApplyStacking(Beatmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ApplyStacking(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
var hitObjects = beatmap.HitObjects as List<OsuHitObject> ?? beatmap.HitObjects.OfType<OsuHitObject>().ToList();
|
||||||
|
|
||||||
if (hitObjects.Count > 0)
|
if (hitObjects.Count > 0)
|
||||||
{
|
{
|
||||||
@ -50,14 +55,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
foreach (var h in hitObjects)
|
foreach (var h in hitObjects)
|
||||||
h.StackHeight = 0;
|
h.StackHeight = 0;
|
||||||
|
|
||||||
if (Beatmap.BeatmapInfo.BeatmapVersion >= 6)
|
if (beatmap.BeatmapInfo.BeatmapVersion >= 6)
|
||||||
applyStacking(Beatmap.BeatmapInfo, hitObjects, 0, hitObjects.Count - 1);
|
applyStacking(beatmap.BeatmapInfo, hitObjects, 0, hitObjects.Count - 1);
|
||||||
else
|
else
|
||||||
applyStackingOld(Beatmap.BeatmapInfo, hitObjects);
|
applyStackingOld(beatmap.BeatmapInfo, hitObjects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyStacking(BeatmapInfo beatmapInfo, List<OsuHitObject> hitObjects, int startIndex, int endIndex)
|
private static void applyStacking(BeatmapInfo beatmapInfo, List<OsuHitObject> hitObjects, int startIndex, int endIndex)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex, endIndex);
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex, endIndex);
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(startIndex);
|
ArgumentOutOfRangeException.ThrowIfNegative(startIndex);
|
||||||
@ -209,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyStackingOld(BeatmapInfo beatmapInfo, List<OsuHitObject> hitObjects)
|
private static void applyStackingOld(BeatmapInfo beatmapInfo, List<OsuHitObject> hitObjects)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < hitObjects.Count; i++)
|
for (int i = 0; i < hitObjects.Count; i++)
|
||||||
{
|
{
|
||||||
|
@ -61,12 +61,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
flashlightRating *= 0.7;
|
flashlightRating *= 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000;
|
double baseAimPerformance = OsuStrainSkill.DifficultyToPerformance(aimRating);
|
||||||
double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000;
|
double baseSpeedPerformance = OsuStrainSkill.DifficultyToPerformance(speedRating);
|
||||||
double baseFlashlightPerformance = 0.0;
|
double baseFlashlightPerformance = 0.0;
|
||||||
|
|
||||||
if (mods.Any(h => h is OsuModFlashlight))
|
if (mods.Any(h => h is OsuModFlashlight))
|
||||||
baseFlashlightPerformance = Math.Pow(flashlightRating, 2.0) * 25.0;
|
baseFlashlightPerformance = Flashlight.DifficultyToPerformance(flashlightRating);
|
||||||
|
|
||||||
double basePerformance =
|
double basePerformance =
|
||||||
Math.Pow(
|
Math.Pow(
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Osu.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -86,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attributes)
|
private double computeAimValue(ScoreInfo score, OsuDifficultyAttributes attributes)
|
||||||
{
|
{
|
||||||
double aimValue = Math.Pow(5.0 * Math.Max(1.0, attributes.AimDifficulty / 0.0675) - 4.0, 3.0) / 100000.0;
|
double aimValue = OsuStrainSkill.DifficultyToPerformance(attributes.AimDifficulty);
|
||||||
|
|
||||||
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
||||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
||||||
@ -139,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
if (score.Mods.Any(h => h is OsuModRelax))
|
if (score.Mods.Any(h => h is OsuModRelax))
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
||||||
double speedValue = Math.Pow(5.0 * Math.Max(1.0, attributes.SpeedDifficulty / 0.0675) - 4.0, 3.0) / 100000.0;
|
double speedValue = OsuStrainSkill.DifficultyToPerformance(attributes.SpeedDifficulty);
|
||||||
|
|
||||||
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
|
||||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
|
||||||
@ -226,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
if (!score.Mods.Any(h => h is OsuModFlashlight))
|
if (!score.Mods.Any(h => h is OsuModFlashlight))
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
||||||
double flashlightValue = Math.Pow(attributes.FlashlightDifficulty, 2.0) * 25.0;
|
double flashlightValue = Flashlight.DifficultyToPerformance(attributes.FlashlightDifficulty);
|
||||||
|
|
||||||
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
// Penalize misses by assessing # of misses relative to the total # of objects. Default a 3% reduction for any # of misses.
|
||||||
if (effectiveMissCount > 0)
|
if (effectiveMissCount > 0)
|
||||||
|
@ -42,5 +42,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override double DifficultyValue() => GetCurrentStrainPeaks().Sum() * OsuStrainSkill.DEFAULT_DIFFICULTY_MULTIPLIER;
|
public override double DifficultyValue() => GetCurrentStrainPeaks().Sum() * OsuStrainSkill.DEFAULT_DIFFICULTY_MULTIPLIER;
|
||||||
|
|
||||||
|
public static double DifficultyToPerformance(double difficulty) => 25 * Math.Pow(difficulty, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,5 +67,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
|
|
||||||
return difficulty * DifficultyMultiplier;
|
return difficulty * DifficultyMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static double DifficultyToPerformance(double difficulty) => Math.Pow(5.0 * Math.Max(1.0, difficulty / 0.0675) - 4.0, 3.0) / 100000.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,6 +295,12 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
if (Vector2.Distance(closestSnapPosition, screenSpacePosition) < snapRadius)
|
if (Vector2.Distance(closestSnapPosition, screenSpacePosition) < snapRadius)
|
||||||
{
|
{
|
||||||
|
// if the snap target is a stacked object, snap to its unstacked position rather than its stacked position.
|
||||||
|
// this is intended to make working with stacks easier (because thanks to this, you can drag an object to any
|
||||||
|
// of the items on the stack to add an object to it, rather than having to drag to the position of the *first* object on it at all times).
|
||||||
|
if (b.Item is OsuHitObject osuObject && osuObject.StackOffset != Vector2.Zero)
|
||||||
|
closestSnapPosition = b.ToScreenSpace(b.ToLocalSpace(closestSnapPosition) - osuObject.StackOffset);
|
||||||
|
|
||||||
// only return distance portion, since time is not really valid
|
// only return distance portion, since time is not really valid
|
||||||
snapResult = new SnapResult(closestSnapPosition, null, playfield);
|
snapResult = new SnapResult(closestSnapPosition, null, playfield);
|
||||||
return true;
|
return true;
|
||||||
|
@ -11,14 +11,14 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
public partial class OsuHitObjectInspector : HitObjectInspector
|
public partial class OsuHitObjectInspector : HitObjectInspector
|
||||||
{
|
{
|
||||||
protected override void AddInspectorValues()
|
protected override void AddInspectorValues(HitObject[] objects)
|
||||||
{
|
{
|
||||||
base.AddInspectorValues();
|
base.AddInspectorValues(objects);
|
||||||
|
|
||||||
if (EditorBeatmap.SelectedHitObjects.Count > 0)
|
if (objects.Length > 0)
|
||||||
{
|
{
|
||||||
var firstInSelection = (OsuHitObject)EditorBeatmap.SelectedHitObjects.MinBy(ho => ho.StartTime)!;
|
var firstInSelection = (OsuHitObject)objects.MinBy(ho => ho.StartTime)!;
|
||||||
var lastInSelection = (OsuHitObject)EditorBeatmap.SelectedHitObjects.MaxBy(ho => ho.GetEndTime())!;
|
var lastInSelection = (OsuHitObject)objects.MaxBy(ho => ho.GetEndTime())!;
|
||||||
|
|
||||||
Debug.Assert(firstInSelection != null && lastInSelection != null);
|
Debug.Assert(firstInSelection != null && lastInSelection != null);
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
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.Rulesets.Osu.Beatmaps;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
@ -50,12 +51,33 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
var hitObjects = selectedMovableObjects;
|
var hitObjects = selectedMovableObjects;
|
||||||
|
|
||||||
|
var localDelta = this.ScreenSpaceDeltaToParentSpace(moveEvent.ScreenSpaceDelta);
|
||||||
|
|
||||||
|
// this conditional is a rather ugly special case for stacks.
|
||||||
|
// as it turns out, adding the `EditorBeatmap.Update()` call at the end of this would cause stacked objects to jitter when moved around
|
||||||
|
// (they would stack and then unstack every frame).
|
||||||
|
// the reason for that is that the selection handling abstractions are not aware of the distinction between "displayed" and "actual" position
|
||||||
|
// which is unique to osu! due to stacking being applied as a post-processing step.
|
||||||
|
// therefore, the following loop would occur:
|
||||||
|
// - on frame 1 the blueprint is snapped to the stack's baseline position. `EditorBeatmap.Update()` applies stacking successfully,
|
||||||
|
// the blueprint moves up the stack from its original drag position.
|
||||||
|
// - on frame 2 the blueprint's position is now the *stacked* position, which is interpreted higher up as *manually performing an unstack*
|
||||||
|
// to the blueprint's unstacked position (as the machinery higher up only cares about differences in screen space position).
|
||||||
|
if (hitObjects.Any(h => Precision.AlmostEquals(localDelta, -h.StackOffset)))
|
||||||
|
return true;
|
||||||
|
|
||||||
// this will potentially move the selection out of bounds...
|
// this will potentially move the selection out of bounds...
|
||||||
foreach (var h in hitObjects)
|
foreach (var h in hitObjects)
|
||||||
h.Position += this.ScreenSpaceDeltaToParentSpace(moveEvent.ScreenSpaceDelta);
|
h.Position += localDelta;
|
||||||
|
|
||||||
// but this will be corrected.
|
// but this will be corrected.
|
||||||
moveSelectionInBounds();
|
moveSelectionInBounds();
|
||||||
|
|
||||||
|
// manually update stacking.
|
||||||
|
// this intentionally bypasses the editor `UpdateState()` / beatmap processor flow for performance reasons,
|
||||||
|
// as the entire flow is too expensive to run on every movement.
|
||||||
|
Scheduler.AddOnce(OsuBeatmapProcessor.ApplyStacking, EditorBeatmap);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -41,139 +42,179 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
||||||
{
|
{
|
||||||
if (lookup is OsuSkinComponentLookup osuComponent)
|
switch (lookup)
|
||||||
{
|
{
|
||||||
switch (osuComponent.Component)
|
case SkinComponentsContainerLookup containerLookup:
|
||||||
{
|
// Only handle per ruleset defaults here.
|
||||||
case OsuSkinComponents.FollowPoint:
|
if (containerLookup.Ruleset == null)
|
||||||
return this.GetAnimation("followpoint", true, true, true, startAtCurrentTime: false, maxSize: new Vector2(OsuHitObject.OBJECT_RADIUS * 2, OsuHitObject.OBJECT_RADIUS));
|
return base.GetDrawableComponent(lookup);
|
||||||
|
|
||||||
case OsuSkinComponents.SliderScorePoint:
|
// Skin has configuration.
|
||||||
return this.GetAnimation("sliderscorepoint", false, false, maxSize: OsuHitObject.OBJECT_DIMENSIONS);
|
if (base.GetDrawableComponent(lookup) is UserConfiguredLayoutContainer d)
|
||||||
|
return d;
|
||||||
|
|
||||||
case OsuSkinComponents.SliderFollowCircle:
|
// Our own ruleset components default.
|
||||||
var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true, maxSize: MAX_FOLLOW_CIRCLE_AREA_SIZE);
|
switch (containerLookup.Target)
|
||||||
if (followCircleContent != null)
|
{
|
||||||
return new LegacyFollowCircle(followCircleContent);
|
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
||||||
|
return new DefaultSkinComponentsContainer(container =>
|
||||||
|
{
|
||||||
|
var keyCounter = container.OfType<LegacyKeyCounterDisplay>().FirstOrDefault();
|
||||||
|
|
||||||
return null;
|
if (keyCounter != null)
|
||||||
|
{
|
||||||
|
// set the anchor to top right so that it won't squash to the return button to the top
|
||||||
|
keyCounter.Anchor = Anchor.CentreRight;
|
||||||
|
keyCounter.Origin = Anchor.CentreRight;
|
||||||
|
keyCounter.X = 0;
|
||||||
|
// 340px is the default height inherit from stable
|
||||||
|
keyCounter.Y = container.ToLocalSpace(new Vector2(0, container.ScreenSpaceDrawQuad.Centre.Y - 340f)).Y;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new LegacyComboCounter(),
|
||||||
|
new LegacyKeyCounterDisplay(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
case OsuSkinComponents.SliderBall:
|
return null;
|
||||||
if (GetTexture("sliderb") != null || GetTexture("sliderb0") != null)
|
|
||||||
return new LegacySliderBall(this);
|
|
||||||
|
|
||||||
return null;
|
case OsuSkinComponentLookup osuComponent:
|
||||||
|
switch (osuComponent.Component)
|
||||||
|
{
|
||||||
|
case OsuSkinComponents.FollowPoint:
|
||||||
|
return this.GetAnimation("followpoint", true, true, true, startAtCurrentTime: false, maxSize: new Vector2(OsuHitObject.OBJECT_RADIUS * 2, OsuHitObject.OBJECT_RADIUS));
|
||||||
|
|
||||||
case OsuSkinComponents.SliderBody:
|
case OsuSkinComponents.SliderScorePoint:
|
||||||
if (hasHitCircle.Value)
|
return this.GetAnimation("sliderscorepoint", false, false, maxSize: OsuHitObject.OBJECT_DIMENSIONS);
|
||||||
return new LegacySliderBody();
|
|
||||||
|
|
||||||
return null;
|
case OsuSkinComponents.SliderFollowCircle:
|
||||||
|
var followCircleContent = this.GetAnimation("sliderfollowcircle", true, true, true, maxSize: MAX_FOLLOW_CIRCLE_AREA_SIZE);
|
||||||
|
if (followCircleContent != null)
|
||||||
|
return new LegacyFollowCircle(followCircleContent);
|
||||||
|
|
||||||
case OsuSkinComponents.SliderTailHitCircle:
|
|
||||||
if (hasHitCircle.Value)
|
|
||||||
return new LegacyMainCirclePiece("sliderendcircle", false);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.SliderHeadHitCircle:
|
|
||||||
if (hasHitCircle.Value)
|
|
||||||
return new LegacySliderHeadHitCircle();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.ReverseArrow:
|
|
||||||
if (hasHitCircle.Value)
|
|
||||||
return new LegacyReverseArrow();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.HitCircle:
|
|
||||||
if (hasHitCircle.Value)
|
|
||||||
return new LegacyMainCirclePiece();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.Cursor:
|
|
||||||
if (GetTexture("cursor") != null)
|
|
||||||
return new LegacyCursor(this);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.CursorTrail:
|
|
||||||
if (GetTexture("cursortrail") != null)
|
|
||||||
return new LegacyCursorTrail(this);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.CursorRipple:
|
|
||||||
if (GetTexture("cursor-ripple") != null)
|
|
||||||
{
|
|
||||||
var ripple = this.GetAnimation("cursor-ripple", false, false);
|
|
||||||
|
|
||||||
// In stable this element was scaled down to 50% and opacity 20%, but this makes the elements WAY too big and inflexible.
|
|
||||||
// If anyone complains about these not being applied, this can be uncommented.
|
|
||||||
//
|
|
||||||
// But if no one complains I'd rather fix this in lazer. Wiki documentation doesn't mention size,
|
|
||||||
// so we might be okay.
|
|
||||||
//
|
|
||||||
// if (ripple != null)
|
|
||||||
// {
|
|
||||||
// ripple.Scale = new Vector2(0.5f);
|
|
||||||
// ripple.Alpha = 0.2f;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return ripple;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.CursorParticles:
|
|
||||||
if (GetTexture("star2") != null)
|
|
||||||
return new LegacyCursorParticles();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.CursorSmoke:
|
|
||||||
if (GetTexture("cursor-smoke") != null)
|
|
||||||
return new LegacySmokeSegment();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.HitCircleText:
|
|
||||||
if (!this.HasFont(LegacyFont.HitCircle))
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const float hitcircle_text_scale = 0.8f;
|
case OsuSkinComponents.SliderBall:
|
||||||
return new LegacySpriteText(LegacyFont.HitCircle)
|
if (GetTexture("sliderb") != null || GetTexture("sliderb0") != null)
|
||||||
{
|
return new LegacySliderBall(this);
|
||||||
// stable applies a blanket 0.8x scale to hitcircle fonts
|
|
||||||
Scale = new Vector2(hitcircle_text_scale),
|
|
||||||
MaxSizePerGlyph = OsuHitObject.OBJECT_DIMENSIONS * 2 / hitcircle_text_scale,
|
|
||||||
};
|
|
||||||
|
|
||||||
case OsuSkinComponents.SpinnerBody:
|
return null;
|
||||||
bool hasBackground = GetTexture("spinner-background") != null;
|
|
||||||
|
|
||||||
if (GetTexture("spinner-top") != null && !hasBackground)
|
case OsuSkinComponents.SliderBody:
|
||||||
return new LegacyNewStyleSpinner();
|
if (hasHitCircle.Value)
|
||||||
else if (hasBackground)
|
return new LegacySliderBody();
|
||||||
return new LegacyOldStyleSpinner();
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case OsuSkinComponents.ApproachCircle:
|
case OsuSkinComponents.SliderTailHitCircle:
|
||||||
if (GetTexture(@"approachcircle") != null)
|
if (hasHitCircle.Value)
|
||||||
return new LegacyApproachCircle();
|
return new LegacyMainCirclePiece("sliderendcircle", false);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
default:
|
case OsuSkinComponents.SliderHeadHitCircle:
|
||||||
throw new UnsupportedSkinComponentException(lookup);
|
if (hasHitCircle.Value)
|
||||||
}
|
return new LegacySliderHeadHitCircle();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.ReverseArrow:
|
||||||
|
if (hasHitCircle.Value)
|
||||||
|
return new LegacyReverseArrow();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.HitCircle:
|
||||||
|
if (hasHitCircle.Value)
|
||||||
|
return new LegacyMainCirclePiece();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.Cursor:
|
||||||
|
if (GetTexture("cursor") != null)
|
||||||
|
return new LegacyCursor(this);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.CursorTrail:
|
||||||
|
if (GetTexture("cursortrail") != null)
|
||||||
|
return new LegacyCursorTrail(this);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.CursorRipple:
|
||||||
|
if (GetTexture("cursor-ripple") != null)
|
||||||
|
{
|
||||||
|
var ripple = this.GetAnimation("cursor-ripple", false, false);
|
||||||
|
|
||||||
|
// In stable this element was scaled down to 50% and opacity 20%, but this makes the elements WAY too big and inflexible.
|
||||||
|
// If anyone complains about these not being applied, this can be uncommented.
|
||||||
|
//
|
||||||
|
// But if no one complains I'd rather fix this in lazer. Wiki documentation doesn't mention size,
|
||||||
|
// so we might be okay.
|
||||||
|
//
|
||||||
|
// if (ripple != null)
|
||||||
|
// {
|
||||||
|
// ripple.Scale = new Vector2(0.5f);
|
||||||
|
// ripple.Alpha = 0.2f;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return ripple;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.CursorParticles:
|
||||||
|
if (GetTexture("star2") != null)
|
||||||
|
return new LegacyCursorParticles();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.CursorSmoke:
|
||||||
|
if (GetTexture("cursor-smoke") != null)
|
||||||
|
return new LegacySmokeSegment();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.HitCircleText:
|
||||||
|
if (!this.HasFont(LegacyFont.HitCircle))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const float hitcircle_text_scale = 0.8f;
|
||||||
|
return new LegacySpriteText(LegacyFont.HitCircle)
|
||||||
|
{
|
||||||
|
// stable applies a blanket 0.8x scale to hitcircle fonts
|
||||||
|
Scale = new Vector2(hitcircle_text_scale),
|
||||||
|
MaxSizePerGlyph = OsuHitObject.OBJECT_DIMENSIONS * 2 / hitcircle_text_scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
case OsuSkinComponents.SpinnerBody:
|
||||||
|
bool hasBackground = GetTexture("spinner-background") != null;
|
||||||
|
|
||||||
|
if (GetTexture("spinner-top") != null && !hasBackground)
|
||||||
|
return new LegacyNewStyleSpinner();
|
||||||
|
else if (hasBackground)
|
||||||
|
return new LegacyOldStyleSpinner();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.ApproachCircle:
|
||||||
|
if (GetTexture(@"approachcircle") != null)
|
||||||
|
return new LegacyApproachCircle();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new UnsupportedSkinComponentException(lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return base.GetDrawableComponent(lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.GetDrawableComponent(lookup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
|
@ -16,7 +16,6 @@ using osu.Framework.Graphics.Shaders.Types;
|
|||||||
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;
|
||||||
@ -63,8 +62,6 @@ 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]
|
||||||
@ -95,12 +92,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly LayoutValue<Vector2> partSizeCache = new LayoutValue<Vector2>(Invalidation.DrawInfo | Invalidation.RequiredParentSizeToFit | Invalidation.Presence);
|
|
||||||
|
|
||||||
private Vector2 partSize => partSizeCache.IsValid
|
|
||||||
? partSizeCache.Value
|
|
||||||
: (partSizeCache.Value = new Vector2(Texture.DisplayWidth, Texture.DisplayHeight) * DrawInfo.Matrix.ExtractScale().Xy);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of time to fade the cursor trail pieces.
|
/// The amount of time to fade the cursor trail pieces.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -156,6 +147,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
protected void AddTrail(Vector2 position)
|
protected void AddTrail(Vector2 position)
|
||||||
{
|
{
|
||||||
|
position = ToLocalSpace(position);
|
||||||
|
|
||||||
if (InterpolateMovements)
|
if (InterpolateMovements)
|
||||||
{
|
{
|
||||||
if (!lastPosition.HasValue)
|
if (!lastPosition.HasValue)
|
||||||
@ -174,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
float distance = diff.Length;
|
float distance = diff.Length;
|
||||||
Vector2 direction = diff / distance;
|
Vector2 direction = diff / distance;
|
||||||
|
|
||||||
float interval = partSize.X / 2.5f * IntervalMultiplier;
|
float interval = Texture.DisplayWidth / 2.5f * IntervalMultiplier;
|
||||||
float stopAt = distance - (AvoidDrawingNearCursor ? interval : 0);
|
float stopAt = distance - (AvoidDrawingNearCursor ? interval : 0);
|
||||||
|
|
||||||
for (float d = interval; d < stopAt; d += interval)
|
for (float d = interval; d < stopAt; d += interval)
|
||||||
@ -191,9 +184,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPart(Vector2 screenSpacePosition)
|
private void addPart(Vector2 localSpacePosition)
|
||||||
{
|
{
|
||||||
parts[currentIndex].Position = ToLocalSpace(screenSpacePosition);
|
parts[currentIndex].Position = localSpacePosition;
|
||||||
parts[currentIndex].Time = time + 1;
|
parts[currentIndex].Time = time + 1;
|
||||||
++parts[currentIndex].InvalidationID;
|
++parts[currentIndex].InvalidationID;
|
||||||
|
|
||||||
|
@ -206,6 +206,15 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => HitObjectContainer.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => HitObjectContainer.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
|
private OsuResumeOverlay.OsuResumeOverlayInputBlocker? resumeInputBlocker;
|
||||||
|
|
||||||
|
public void AttachResumeOverlayInputBlocker(OsuResumeOverlay.OsuResumeOverlayInputBlocker resumeInputBlocker)
|
||||||
|
{
|
||||||
|
Debug.Assert(this.resumeInputBlocker == null);
|
||||||
|
this.resumeInputBlocker = resumeInputBlocker;
|
||||||
|
AddInternal(resumeInputBlocker);
|
||||||
|
}
|
||||||
|
|
||||||
private partial class ProxyContainer : LifetimeManagementContainer
|
private partial class ProxyContainer : LifetimeManagementContainer
|
||||||
{
|
{
|
||||||
public void Add(Drawable proxy) => AddInternal(proxy);
|
public void Add(Drawable proxy) => AddInternal(proxy);
|
||||||
|
@ -33,9 +33,30 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
OsuResumeOverlayInputBlocker? inputBlocker = null;
|
||||||
|
|
||||||
|
if (drawableRuleset != null)
|
||||||
|
{
|
||||||
|
var osuPlayfield = (OsuPlayfield)drawableRuleset.Playfield;
|
||||||
|
osuPlayfield.AttachResumeOverlayInputBlocker(inputBlocker = new OsuResumeOverlayInputBlocker());
|
||||||
|
}
|
||||||
|
|
||||||
Add(cursorScaleContainer = new Container
|
Add(cursorScaleContainer = new Container
|
||||||
{
|
{
|
||||||
Child = clickToResumeCursor = new OsuClickToResumeCursor { ResumeRequested = Resume }
|
Child = clickToResumeCursor = new OsuClickToResumeCursor
|
||||||
|
{
|
||||||
|
ResumeRequested = () =>
|
||||||
|
{
|
||||||
|
// since the user had to press a button to tap the resume cursor,
|
||||||
|
// block that press event from potentially reaching a hit circle that's behind the cursor.
|
||||||
|
// we cannot do this from OsuClickToResumeCursor directly since we're in a different input manager tree than the gameplay one,
|
||||||
|
// so we rely on a dedicated input blocking component that's implanted in there to do that for us.
|
||||||
|
if (inputBlocker != null)
|
||||||
|
inputBlocker.BlockNextPress = true;
|
||||||
|
|
||||||
|
Resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,10 +136,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
scaleTransitionContainer.ScaleTo(2, TRANSITION_TIME, Easing.OutQuint);
|
scaleTransitionContainer.ScaleTo(2, TRANSITION_TIME, Easing.OutQuint);
|
||||||
|
ResumeRequested?.Invoke();
|
||||||
// When resuming with a button, we do not want the osu! input manager to see this button press and include it in the score.
|
|
||||||
// To ensure that this works correctly, schedule the resume operation one frame forward, since the resume operation enables the input manager to see input events.
|
|
||||||
Schedule(() => ResumeRequested?.Invoke());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,5 +161,27 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
this.FadeColour(IsHovered ? Color4.White : Color4.Orange, 400, Easing.OutQuint);
|
this.FadeColour(IsHovered ? Color4.White : Color4.Orange, 400, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public partial class OsuResumeOverlayInputBlocker : Drawable, IKeyBindingHandler<OsuAction>
|
||||||
|
{
|
||||||
|
public bool BlockNextPress;
|
||||||
|
|
||||||
|
public OsuResumeOverlayInputBlocker()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Depth = float.MinValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||||
|
{
|
||||||
|
bool block = BlockNextPress;
|
||||||
|
BlockNextPress = false;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Drawable? GetDrawableComponent(ISkinComponentLookup component)
|
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
||||||
{
|
{
|
||||||
switch (component)
|
switch (lookup)
|
||||||
{
|
{
|
||||||
case GameplaySkinComponentLookup<HitResult> resultComponent:
|
case GameplaySkinComponentLookup<HitResult> resultComponent:
|
||||||
// This should eventually be moved to a skin setting, when supported.
|
// This should eventually be moved to a skin setting, when supported.
|
||||||
@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.GetDrawableComponent(component);
|
return base.GetDrawableComponent(lookup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -468,6 +468,40 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeBeatmapHitObjectCoordinatesLegacy()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyBeatmapDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("hitobject-coordinates-legacy.osu"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||||
|
|
||||||
|
var positionData = hitObjects[0] as IHasPosition;
|
||||||
|
|
||||||
|
Assert.IsNotNull(positionData);
|
||||||
|
Assert.AreEqual(new Vector2(256, 256), positionData!.Position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeBeatmapHitObjectCoordinatesLazer()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyBeatmapDecoder(LegacyBeatmapEncoder.FIRST_LAZER_VERSION);
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("hitobject-coordinates-lazer.osu"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||||
|
|
||||||
|
var positionData = hitObjects[0] as IHasPosition;
|
||||||
|
|
||||||
|
Assert.IsNotNull(positionData);
|
||||||
|
Assert.AreEqual(new Vector2(256.99853f, 256.001f), positionData!.Position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeBeatmapHitObjects()
|
public void TestDecodeBeatmapHitObjects()
|
||||||
{
|
{
|
||||||
|
@ -259,6 +259,44 @@ namespace osu.Game.Tests.Database
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoChangesAfterDelete()
|
||||||
|
{
|
||||||
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
|
{
|
||||||
|
var importer = new BeatmapImporter(storage, realm);
|
||||||
|
using var rulesets = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
|
using var __ = getBeatmapArchive(out string pathOriginal);
|
||||||
|
using var _ = getBeatmapArchive(out string pathOriginalSecond);
|
||||||
|
|
||||||
|
var importBeforeUpdate = await importer.Import(new ImportTask(pathOriginal));
|
||||||
|
|
||||||
|
importBeforeUpdate!.PerformWrite(s => s.DeletePending = true);
|
||||||
|
|
||||||
|
var dateBefore = importBeforeUpdate.Value.DateAdded;
|
||||||
|
|
||||||
|
Assert.That(importBeforeUpdate, Is.Not.Null);
|
||||||
|
Debug.Assert(importBeforeUpdate != null);
|
||||||
|
|
||||||
|
var importAfterUpdate = await importer.ImportAsUpdate(new ProgressNotification(), new ImportTask(pathOriginalSecond), importBeforeUpdate.Value);
|
||||||
|
|
||||||
|
realm.Run(r => r.Refresh());
|
||||||
|
|
||||||
|
Assert.That(importAfterUpdate, Is.Not.Null);
|
||||||
|
Debug.Assert(importAfterUpdate != null);
|
||||||
|
|
||||||
|
checkCount<BeatmapSetInfo>(realm, 1);
|
||||||
|
checkCount<BeatmapInfo>(realm, count_beatmaps);
|
||||||
|
checkCount<BeatmapMetadata>(realm, count_beatmaps);
|
||||||
|
|
||||||
|
Assert.That(importBeforeUpdate.Value.Beatmaps.First().OnlineID, Is.GreaterThan(-1));
|
||||||
|
Assert.That(importBeforeUpdate.Value.DateAdded, Is.EqualTo(dateBefore));
|
||||||
|
Assert.That(importAfterUpdate.Value.DateAdded, Is.EqualTo(dateBefore));
|
||||||
|
Assert.That(importBeforeUpdate.ID, Is.EqualTo(importAfterUpdate.ID));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestNoChanges()
|
public void TestNoChanges()
|
||||||
{
|
{
|
||||||
@ -272,21 +310,25 @@ namespace osu.Game.Tests.Database
|
|||||||
|
|
||||||
var importBeforeUpdate = await importer.Import(new ImportTask(pathOriginal));
|
var importBeforeUpdate = await importer.Import(new ImportTask(pathOriginal));
|
||||||
|
|
||||||
|
var dateBefore = importBeforeUpdate!.Value.DateAdded;
|
||||||
|
|
||||||
Assert.That(importBeforeUpdate, Is.Not.Null);
|
Assert.That(importBeforeUpdate, Is.Not.Null);
|
||||||
Debug.Assert(importBeforeUpdate != null);
|
Debug.Assert(importBeforeUpdate != null);
|
||||||
|
|
||||||
var importAfterUpdate = await importer.ImportAsUpdate(new ProgressNotification(), new ImportTask(pathOriginalSecond), importBeforeUpdate.Value);
|
var importAfterUpdate = await importer.ImportAsUpdate(new ProgressNotification(), new ImportTask(pathOriginalSecond), importBeforeUpdate.Value);
|
||||||
|
|
||||||
|
realm.Run(r => r.Refresh());
|
||||||
|
|
||||||
Assert.That(importAfterUpdate, Is.Not.Null);
|
Assert.That(importAfterUpdate, Is.Not.Null);
|
||||||
Debug.Assert(importAfterUpdate != null);
|
Debug.Assert(importAfterUpdate != null);
|
||||||
|
|
||||||
realm.Run(r => r.Refresh());
|
|
||||||
|
|
||||||
checkCount<BeatmapSetInfo>(realm, 1);
|
checkCount<BeatmapSetInfo>(realm, 1);
|
||||||
checkCount<BeatmapInfo>(realm, count_beatmaps);
|
checkCount<BeatmapInfo>(realm, count_beatmaps);
|
||||||
checkCount<BeatmapMetadata>(realm, count_beatmaps);
|
checkCount<BeatmapMetadata>(realm, count_beatmaps);
|
||||||
|
|
||||||
Assert.That(importBeforeUpdate.Value.Beatmaps.First().OnlineID, Is.GreaterThan(-1));
|
Assert.That(importBeforeUpdate.Value.Beatmaps.First().OnlineID, Is.GreaterThan(-1));
|
||||||
|
Assert.That(importBeforeUpdate.Value.DateAdded, Is.EqualTo(dateBefore));
|
||||||
|
Assert.That(importAfterUpdate.Value.DateAdded, Is.EqualTo(dateBefore));
|
||||||
Assert.That(importBeforeUpdate.ID, Is.EqualTo(importAfterUpdate.ID));
|
Assert.That(importBeforeUpdate.ID, Is.EqualTo(importAfterUpdate.ID));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -52,10 +52,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup() => Schedule(() =>
|
public void Setup() => Schedule(() =>
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Child = composer = new TestHitObjectComposer();
|
||||||
{
|
|
||||||
composer = new TestHitObjectComposer()
|
|
||||||
};
|
|
||||||
|
|
||||||
BeatDivisor.Value = 1;
|
BeatDivisor.Value = 1;
|
||||||
|
|
||||||
|
BIN
osu.Game.Tests/Resources/Archives/argon-layout-version-0.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/argon-layout-version-0.osk
Normal file
Binary file not shown.
BIN
osu.Game.Tests/Resources/Archives/classic-layout-version-0.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/classic-layout-version-0.osk
Normal file
Binary file not shown.
BIN
osu.Game.Tests/Resources/Archives/modified-classic-20240724.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/modified-classic-20240724.osk
Normal file
Binary file not shown.
BIN
osu.Game.Tests/Resources/Archives/triangles-layout-version-0.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/triangles-layout-version-0.osk
Normal file
Binary file not shown.
@ -73,7 +73,12 @@ namespace osu.Game.Tests.Resources
|
|||||||
|
|
||||||
private static string getTempFilename() => temp_storage.GetFullPath(Guid.NewGuid() + ".osz");
|
private static string getTempFilename() => temp_storage.GetFullPath(Guid.NewGuid() + ".osz");
|
||||||
|
|
||||||
private static int importId;
|
private static int testId = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a unique int value which is incremented each call.
|
||||||
|
/// </summary>
|
||||||
|
public static int GetNextTestID() => Interlocked.Increment(ref testId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a test beatmap set model.
|
/// Create a test beatmap set model.
|
||||||
@ -88,7 +93,7 @@ namespace osu.Game.Tests.Resources
|
|||||||
|
|
||||||
RulesetInfo getRuleset() => rulesets?[j++ % rulesets.Length];
|
RulesetInfo getRuleset() => rulesets?[j++ % rulesets.Length];
|
||||||
|
|
||||||
int setId = Interlocked.Increment(ref importId);
|
int setId = GetNextTestID();
|
||||||
|
|
||||||
var metadata = new BeatmapMetadata
|
var metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
|
6
osu.Game.Tests/Resources/hitobject-coordinates-lazer.osu
Normal file
6
osu.Game.Tests/Resources/hitobject-coordinates-lazer.osu
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
osu file format v128
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
// Coordinates should be preserves in lazer beatmaps.
|
||||||
|
|
||||||
|
256.99853,256.001,1000,49,0,0:0:0:0:
|
@ -0,0 +1,5 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
// Coordinates should be truncated to int values in legacy beatmaps.
|
||||||
|
256.99853,256.001,1000,49,0,0:0:0:0:
|
@ -65,7 +65,9 @@ namespace osu.Game.Tests.Skins
|
|||||||
// Covers default rank display
|
// Covers default rank display
|
||||||
"Archives/modified-default-20230809.osk",
|
"Archives/modified-default-20230809.osk",
|
||||||
// Covers legacy rank display
|
// Covers legacy rank display
|
||||||
"Archives/modified-classic-20230809.osk"
|
"Archives/modified-classic-20230809.osk",
|
||||||
|
// Covers legacy key counter
|
||||||
|
"Archives/modified-classic-20240724.osk"
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -304,11 +304,6 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
{
|
{
|
||||||
private bool? lastLoadTriggerCausedChange;
|
private bool? lastLoadTriggerCausedChange;
|
||||||
|
|
||||||
public TestBackgroundScreenDefault()
|
|
||||||
: base(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Next()
|
public override bool Next()
|
||||||
{
|
{
|
||||||
bool didChange = base.Next();
|
bool didChange = base.Next();
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.Metadata;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
using osu.Game.Tests.Visual.Metadata;
|
||||||
|
using osu.Game.Tests.Visual.OnlinePlay;
|
||||||
|
using CreateRoomRequest = osu.Game.Online.Rooms.CreateRoomRequest;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.DailyChallenge
|
||||||
|
{
|
||||||
|
public partial class TestSceneDailyChallengeIntro : OnlinePlayTestScene
|
||||||
|
{
|
||||||
|
[Cached(typeof(MetadataClient))]
|
||||||
|
private TestMetadataClient metadataClient = new TestMetadataClient();
|
||||||
|
|
||||||
|
[Cached(typeof(INotificationOverlay))]
|
||||||
|
private NotificationOverlay notificationOverlay = new NotificationOverlay();
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
base.Content.Add(notificationOverlay);
|
||||||
|
base.Content.Add(metadataClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Solo]
|
||||||
|
public void TestDailyChallenge()
|
||||||
|
{
|
||||||
|
var room = new Room
|
||||||
|
{
|
||||||
|
RoomID = { Value = 1234 },
|
||||||
|
Name = { Value = "Daily Challenge: June 4, 2024" },
|
||||||
|
Playlist =
|
||||||
|
{
|
||||||
|
new PlaylistItem(CreateAPIBeatmapSet().Beatmaps.First())
|
||||||
|
{
|
||||||
|
RequiredMods = [new APIMod(new OsuModTraceable())],
|
||||||
|
AllowedMods = [new APIMod(new OsuModDoubleTime())]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
EndDate = { Value = DateTimeOffset.Now.AddHours(12) },
|
||||||
|
Category = { Value = RoomCategory.DailyChallenge }
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("add room", () => API.Perform(new CreateRoomRequest(room)));
|
||||||
|
AddStep("push screen", () => LoadScreen(new Screens.OnlinePlay.DailyChallenge.DailyChallengeIntro(room)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNotifications()
|
||||||
|
{
|
||||||
|
var room = new Room
|
||||||
|
{
|
||||||
|
RoomID = { Value = 1234 },
|
||||||
|
Name = { Value = "Daily Challenge: June 4, 2024" },
|
||||||
|
Playlist =
|
||||||
|
{
|
||||||
|
new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First())
|
||||||
|
{
|
||||||
|
RequiredMods = [new APIMod(new OsuModTraceable())],
|
||||||
|
AllowedMods = [new APIMod(new OsuModDoubleTime())]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
EndDate = { Value = DateTimeOffset.Now.AddHours(12) },
|
||||||
|
Category = { Value = RoomCategory.DailyChallenge }
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("add room", () => API.Perform(new CreateRoomRequest(room)));
|
||||||
|
AddStep("set daily challenge info", () => metadataClient.DailyChallengeInfo.Value = new DailyChallengeInfo { RoomID = 1234 });
|
||||||
|
|
||||||
|
Screens.OnlinePlay.DailyChallenge.DailyChallenge screen = null!;
|
||||||
|
AddStep("push screen", () => LoadScreen(screen = new Screens.OnlinePlay.DailyChallenge.DailyChallenge(room)));
|
||||||
|
AddUntilStep("wait for screen", () => screen.IsCurrentScreen());
|
||||||
|
AddStep("daily challenge ended", () => metadataClient.DailyChallengeInfo.Value = null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,74 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
private GlobalActionContainer globalActionContainer => this.ChildrenOfType<GlobalActionContainer>().Single();
|
private GlobalActionContainer globalActionContainer => this.ChildrenOfType<GlobalActionContainer>().Single();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeleteUsingMiddleMouse()
|
||||||
|
{
|
||||||
|
AddStep("select circle placement tool", () => InputManager.Key(Key.Number2));
|
||||||
|
AddStep("move mouse to center of playfield", () => InputManager.MoveMouseTo(this.ChildrenOfType<Playfield>().Single()));
|
||||||
|
AddStep("place circle", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("one circle added", () => EditorBeatmap.HitObjects, () => Has.One.Items);
|
||||||
|
AddStep("delete with middle mouse", () => InputManager.Click(MouseButton.Middle));
|
||||||
|
AddAssert("circle removed", () => EditorBeatmap.HitObjects, () => Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeleteUsingShiftRightClick()
|
||||||
|
{
|
||||||
|
AddStep("select circle placement tool", () => InputManager.Key(Key.Number2));
|
||||||
|
AddStep("move mouse to center of playfield", () => InputManager.MoveMouseTo(this.ChildrenOfType<Playfield>().Single()));
|
||||||
|
AddStep("place circle", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("one circle added", () => EditorBeatmap.HitObjects, () => Has.One.Items);
|
||||||
|
AddStep("delete with right mouse", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ShiftLeft);
|
||||||
|
InputManager.Click(MouseButton.Right);
|
||||||
|
InputManager.ReleaseKey(Key.ShiftLeft);
|
||||||
|
});
|
||||||
|
AddAssert("circle removed", () => EditorBeatmap.HitObjects, () => Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestContextMenu()
|
||||||
|
{
|
||||||
|
AddStep("select circle placement tool", () => InputManager.Key(Key.Number2));
|
||||||
|
AddStep("move mouse to center of playfield", () => InputManager.MoveMouseTo(this.ChildrenOfType<Playfield>().Single()));
|
||||||
|
AddStep("place circle", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("one circle added", () => EditorBeatmap.HitObjects, () => Has.One.Items);
|
||||||
|
AddStep("delete with right mouse", () =>
|
||||||
|
{
|
||||||
|
InputManager.Click(MouseButton.Right);
|
||||||
|
});
|
||||||
|
AddAssert("circle not removed", () => EditorBeatmap.HitObjects, () => Has.One.Items);
|
||||||
|
AddAssert("circle selected", () => EditorBeatmap.SelectedHitObjects, () => Has.One.Items);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Solo]
|
||||||
|
public void TestCommitPlacementViaRightClick()
|
||||||
|
{
|
||||||
|
Playfield playfield = null!;
|
||||||
|
|
||||||
|
AddStep("select slider placement tool", () => InputManager.Key(Key.Number3));
|
||||||
|
AddStep("move mouse to top left of playfield", () =>
|
||||||
|
{
|
||||||
|
playfield = this.ChildrenOfType<Playfield>().Single();
|
||||||
|
var location = (3 * playfield.ScreenSpaceDrawQuad.TopLeft + playfield.ScreenSpaceDrawQuad.BottomRight) / 4;
|
||||||
|
InputManager.MoveMouseTo(location);
|
||||||
|
});
|
||||||
|
AddStep("begin placement", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddStep("move mouse to bottom right of playfield", () =>
|
||||||
|
{
|
||||||
|
var location = (playfield.ScreenSpaceDrawQuad.TopLeft + 3 * playfield.ScreenSpaceDrawQuad.BottomRight) / 4;
|
||||||
|
InputManager.MoveMouseTo(location);
|
||||||
|
});
|
||||||
|
AddStep("confirm via right click", () => InputManager.Click(MouseButton.Right));
|
||||||
|
AddAssert("slider placed", () => EditorBeatmap.HitObjects.Count, () => Is.EqualTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCommitPlacementViaGlobalAction()
|
public void TestCommitPlacementViaGlobalAction()
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -45,7 +44,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
// best way to check without exposing.
|
// best way to check without exposing.
|
||||||
private Drawable hideTarget => hudOverlay.ChildrenOfType<SkinComponentsContainer>().First();
|
private Drawable hideTarget => hudOverlay.ChildrenOfType<SkinComponentsContainer>().First();
|
||||||
private Drawable keyCounterFlow => hudOverlay.ChildrenOfType<KeyCounterDisplay>().First().ChildrenOfType<FillFlowContainer<KeyCounter>>().Single();
|
private Drawable keyCounterContent => hudOverlay.ChildrenOfType<KeyCounterDisplay>().First().ChildrenOfType<Drawable>().Skip(1).First();
|
||||||
|
|
||||||
public TestSceneHUDOverlay()
|
public TestSceneHUDOverlay()
|
||||||
{
|
{
|
||||||
@ -79,7 +78,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddAssert("showhud is set", () => hudOverlay.ShowHud.Value);
|
AddAssert("showhud is set", () => hudOverlay.ShowHud.Value);
|
||||||
|
|
||||||
AddAssert("hidetarget is visible", () => hideTarget.Alpha, () => Is.GreaterThan(0));
|
AddAssert("hidetarget is visible", () => hideTarget.Alpha, () => Is.GreaterThan(0));
|
||||||
AddAssert("key counter flow is visible", () => keyCounterFlow.IsPresent);
|
AddAssert("key counter flow is visible", () => keyCounterContent.IsPresent);
|
||||||
AddAssert("pause button is visible", () => hudOverlay.HoldToQuit.IsPresent);
|
AddAssert("pause button is visible", () => hudOverlay.HoldToQuit.IsPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +103,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddAssert("pause button is still visible", () => hudOverlay.HoldToQuit.IsPresent);
|
AddAssert("pause button is still visible", () => hudOverlay.HoldToQuit.IsPresent);
|
||||||
|
|
||||||
// Key counter flow container should not be affected by this, only the key counter display will be hidden as checked above.
|
// Key counter flow container should not be affected by this, only the key counter display will be hidden as checked above.
|
||||||
AddAssert("key counter flow not affected", () => keyCounterFlow.IsPresent);
|
AddAssert("key counter flow not affected", () => keyCounterContent.IsPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -150,11 +149,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
|
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
|
||||||
AddUntilStep("hidetarget is hidden", () => hideTarget.Alpha, () => Is.LessThanOrEqualTo(0));
|
AddUntilStep("hidetarget is hidden", () => hideTarget.Alpha, () => Is.LessThanOrEqualTo(0));
|
||||||
AddUntilStep("key counters hidden", () => !keyCounterFlow.IsPresent);
|
AddUntilStep("key counters hidden", () => !keyCounterContent.IsPresent);
|
||||||
|
|
||||||
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
|
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
|
||||||
AddUntilStep("hidetarget is visible", () => hideTarget.Alpha, () => Is.GreaterThan(0));
|
AddUntilStep("hidetarget is visible", () => hideTarget.Alpha, () => Is.GreaterThan(0));
|
||||||
AddUntilStep("key counters still hidden", () => !keyCounterFlow.IsPresent);
|
AddUntilStep("key counters still hidden", () => !keyCounterContent.IsPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
@ -56,6 +57,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Scale = new Vector2(1, -1)
|
Scale = new Vector2(1, -1)
|
||||||
},
|
},
|
||||||
|
new LegacyKeyCounterDisplay
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
@ -89,6 +95,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Rotation = 90,
|
Rotation = 90,
|
||||||
},
|
},
|
||||||
|
new LegacyKeyCounterDisplay
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Rotation = 90,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
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.Graphics;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -13,6 +14,7 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mania;
|
using osu.Game.Rulesets.Mania;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
@ -32,6 +34,23 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private AudioManager audioManager { get; set; } = null!;
|
private AudioManager audioManager { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||||
|
StartTime = 0,
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||||
|
StartTime = 5000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null) =>
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null) =>
|
||||||
new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||||
|
|
||||||
@ -70,18 +89,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("resume", () => Player.Resume());
|
AddStep("resume", () => Player.Resume());
|
||||||
AddStep("go to resume cursor", () => InputManager.MoveMouseTo(this.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single()));
|
AddStep("go to resume cursor", () => InputManager.MoveMouseTo(this.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single()));
|
||||||
AddStep("press Z to resume", () => InputManager.PressKey(Key.Z));
|
AddStep("press Z to resume", () => InputManager.PressKey(Key.Z));
|
||||||
|
|
||||||
// Z key was released before pause, resuming should not trigger it
|
|
||||||
checkKey(() => counter, 1, false);
|
|
||||||
|
|
||||||
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
|
||||||
checkKey(() => counter, 1, false);
|
|
||||||
|
|
||||||
AddStep("press Z", () => InputManager.PressKey(Key.Z));
|
|
||||||
checkKey(() => counter, 2, true);
|
checkKey(() => counter, 2, true);
|
||||||
|
|
||||||
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
||||||
checkKey(() => counter, 2, false);
|
checkKey(() => counter, 2, false);
|
||||||
|
|
||||||
|
AddStep("press Z", () => InputManager.PressKey(Key.Z));
|
||||||
|
checkKey(() => counter, 3, true);
|
||||||
|
|
||||||
|
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
||||||
|
checkKey(() => counter, 3, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -90,30 +107,29 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
KeyCounter counter = null!;
|
KeyCounter counter = null!;
|
||||||
|
|
||||||
loadPlayer(() => new ManiaRuleset());
|
loadPlayer(() => new ManiaRuleset());
|
||||||
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Key1));
|
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Special1));
|
||||||
checkKey(() => counter, 0, false);
|
checkKey(() => counter, 0, false);
|
||||||
|
|
||||||
AddStep("press D", () => InputManager.PressKey(Key.D));
|
AddStep("press space", () => InputManager.PressKey(Key.Space));
|
||||||
checkKey(() => counter, 1, true);
|
checkKey(() => counter, 1, true);
|
||||||
|
|
||||||
AddStep("release D", () => InputManager.ReleaseKey(Key.D));
|
AddStep("release space", () => InputManager.ReleaseKey(Key.Space));
|
||||||
checkKey(() => counter, 1, false);
|
checkKey(() => counter, 1, false);
|
||||||
|
|
||||||
AddStep("pause", () => Player.Pause());
|
AddStep("pause", () => Player.Pause());
|
||||||
AddStep("press D", () => InputManager.PressKey(Key.D));
|
AddStep("press space", () => InputManager.PressKey(Key.Space));
|
||||||
checkKey(() => counter, 1, false);
|
checkKey(() => counter, 1, false);
|
||||||
|
|
||||||
AddStep("release D", () => InputManager.ReleaseKey(Key.D));
|
AddStep("release space", () => InputManager.ReleaseKey(Key.Space));
|
||||||
checkKey(() => counter, 1, false);
|
checkKey(() => counter, 1, false);
|
||||||
|
|
||||||
AddStep("resume", () => Player.Resume());
|
AddStep("resume", () => Player.Resume());
|
||||||
AddUntilStep("wait for resume", () => Player.GameplayClockContainer.IsRunning);
|
AddUntilStep("wait for resume", () => Player.GameplayClockContainer.IsRunning);
|
||||||
checkKey(() => counter, 1, false);
|
|
||||||
|
|
||||||
AddStep("press D", () => InputManager.PressKey(Key.D));
|
AddStep("press space", () => InputManager.PressKey(Key.Space));
|
||||||
checkKey(() => counter, 2, true);
|
checkKey(() => counter, 2, true);
|
||||||
|
|
||||||
AddStep("release D", () => InputManager.ReleaseKey(Key.D));
|
AddStep("release space", () => InputManager.ReleaseKey(Key.Space));
|
||||||
checkKey(() => counter, 2, false);
|
checkKey(() => counter, 2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,8 +161,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("resume", () => Player.Resume());
|
AddStep("resume", () => Player.Resume());
|
||||||
AddStep("go to resume cursor", () => InputManager.MoveMouseTo(this.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single()));
|
AddStep("go to resume cursor", () => InputManager.MoveMouseTo(this.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single()));
|
||||||
AddStep("press Z to resume", () => InputManager.PressKey(Key.Z));
|
AddStep("press Z to resume", () => InputManager.PressKey(Key.Z));
|
||||||
checkKey(() => counterZ, 1, false);
|
checkKey(() => counterZ, 2, true);
|
||||||
checkKey(() => counterX, 1, false);
|
checkKey(() => counterX, 1, false);
|
||||||
|
|
||||||
|
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
||||||
|
checkKey(() => counterZ, 2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -155,12 +174,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
KeyCounter counter = null!;
|
KeyCounter counter = null!;
|
||||||
|
|
||||||
loadPlayer(() => new ManiaRuleset());
|
loadPlayer(() => new ManiaRuleset());
|
||||||
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Key1));
|
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Special1));
|
||||||
|
|
||||||
AddStep("press D", () => InputManager.PressKey(Key.D));
|
AddStep("press space", () => InputManager.PressKey(Key.Space));
|
||||||
AddStep("pause", () => Player.Pause());
|
AddStep("pause", () => Player.Pause());
|
||||||
|
|
||||||
AddStep("release D", () => InputManager.ReleaseKey(Key.D));
|
AddStep("release space", () => InputManager.ReleaseKey(Key.Space));
|
||||||
checkKey(() => counter, 1, true);
|
checkKey(() => counter, 1, true);
|
||||||
|
|
||||||
AddStep("resume", () => Player.Resume());
|
AddStep("resume", () => Player.Resume());
|
||||||
@ -202,12 +221,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("resume", () => Player.Resume());
|
AddStep("resume", () => Player.Resume());
|
||||||
AddStep("go to resume cursor", () => InputManager.MoveMouseTo(this.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single()));
|
AddStep("go to resume cursor", () => InputManager.MoveMouseTo(this.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single()));
|
||||||
AddStep("press Z to resume", () => InputManager.PressKey(Key.Z));
|
AddStep("press Z to resume", () => InputManager.PressKey(Key.Z));
|
||||||
checkKey(() => counterZ, 1, false);
|
checkKey(() => counterZ, 2, true);
|
||||||
checkKey(() => counterX, 1, true);
|
checkKey(() => counterX, 1, true);
|
||||||
|
|
||||||
AddStep("release X", () => InputManager.ReleaseKey(Key.X));
|
AddStep("release X", () => InputManager.ReleaseKey(Key.X));
|
||||||
checkKey(() => counterZ, 1, false);
|
|
||||||
checkKey(() => counterX, 1, false);
|
checkKey(() => counterX, 1, false);
|
||||||
|
|
||||||
|
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
||||||
|
checkKey(() => counterZ, 2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -216,24 +237,50 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
KeyCounter counter = null!;
|
KeyCounter counter = null!;
|
||||||
|
|
||||||
loadPlayer(() => new ManiaRuleset());
|
loadPlayer(() => new ManiaRuleset());
|
||||||
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Key1));
|
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Special1));
|
||||||
|
|
||||||
AddStep("press D", () => InputManager.PressKey(Key.D));
|
AddStep("press space", () => InputManager.PressKey(Key.Space));
|
||||||
checkKey(() => counter, 1, true);
|
checkKey(() => counter, 1, true);
|
||||||
|
|
||||||
AddStep("pause", () => Player.Pause());
|
AddStep("pause", () => Player.Pause());
|
||||||
|
|
||||||
AddStep("release D", () => InputManager.ReleaseKey(Key.D));
|
AddStep("release space", () => InputManager.ReleaseKey(Key.Space));
|
||||||
AddStep("press D", () => InputManager.PressKey(Key.D));
|
AddStep("press space", () => InputManager.PressKey(Key.Space));
|
||||||
|
|
||||||
AddStep("resume", () => Player.Resume());
|
AddStep("resume", () => Player.Resume());
|
||||||
AddUntilStep("wait for resume", () => Player.GameplayClockContainer.IsRunning);
|
AddUntilStep("wait for resume", () => Player.GameplayClockContainer.IsRunning);
|
||||||
checkKey(() => counter, 1, true);
|
checkKey(() => counter, 1, true);
|
||||||
|
|
||||||
AddStep("release D", () => InputManager.ReleaseKey(Key.D));
|
AddStep("release space", () => InputManager.ReleaseKey(Key.Space));
|
||||||
checkKey(() => counter, 1, false);
|
checkKey(() => counter, 1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOsuRegisterInputFromPressingOrangeCursorButPressIsBlocked()
|
||||||
|
{
|
||||||
|
KeyCounter counter = null!;
|
||||||
|
|
||||||
|
loadPlayer(() => new OsuRuleset());
|
||||||
|
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<OsuAction> actionTrigger && actionTrigger.Action == OsuAction.LeftButton));
|
||||||
|
|
||||||
|
AddStep("pause", () => Player.Pause());
|
||||||
|
AddStep("resume", () => Player.Resume());
|
||||||
|
AddStep("go to resume cursor", () => InputManager.MoveMouseTo(this.ChildrenOfType<OsuResumeOverlay.OsuClickToResumeCursor>().Single()));
|
||||||
|
AddStep("press Z to resume", () => InputManager.PressKey(Key.Z));
|
||||||
|
|
||||||
|
// ensure the input manager receives the Z button press...
|
||||||
|
checkKey(() => counter, 1, true);
|
||||||
|
AddAssert("button is pressed in kbc", () => Player.DrawableRuleset.Playfield.FindClosestParent<OsuInputManager>()!.PressedActions.Single() == OsuAction.LeftButton);
|
||||||
|
|
||||||
|
// ...but also ensure the hit circle in front of the cursor isn't hit by checking max combo.
|
||||||
|
AddAssert("circle not hit", () => Player.ScoreProcessor.HighestCombo.Value, () => Is.EqualTo(0));
|
||||||
|
|
||||||
|
AddStep("release Z", () => InputManager.ReleaseKey(Key.Z));
|
||||||
|
|
||||||
|
checkKey(() => counter, 1, false);
|
||||||
|
AddAssert("button is released in kbc", () => !Player.DrawableRuleset.Playfield.FindClosestParent<OsuInputManager>()!.PressedActions.Any());
|
||||||
|
}
|
||||||
|
|
||||||
private void loadPlayer(Func<Ruleset> createRuleset)
|
private void loadPlayer(Func<Ruleset> createRuleset)
|
||||||
{
|
{
|
||||||
AddStep("set ruleset", () => currentRuleset = createRuleset());
|
AddStep("set ruleset", () => currentRuleset = createRuleset());
|
||||||
@ -241,9 +288,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1);
|
AddUntilStep("player loaded", () => Player.IsLoaded && Player.Alpha == 1);
|
||||||
AddUntilStep("wait for hud", () => Player.HUDOverlay.ChildrenOfType<SkinComponentsContainer>().All(s => s.ComponentsLoaded));
|
AddUntilStep("wait for hud", () => Player.HUDOverlay.ChildrenOfType<SkinComponentsContainer>().All(s => s.ComponentsLoaded));
|
||||||
|
|
||||||
AddStep("seek to gameplay", () => Player.GameplayClockContainer.Seek(20000));
|
AddStep("seek to gameplay", () => Player.GameplayClockContainer.Seek(0));
|
||||||
AddUntilStep("wait for seek to finish", () => Player.DrawableRuleset.FrameStableClock.CurrentTime, () => Is.EqualTo(20000).Within(500));
|
AddUntilStep("wait for seek to finish", () => Player.DrawableRuleset.FrameStableClock.CurrentTime, () => Is.EqualTo(0).Within(500));
|
||||||
AddAssert("not in break", () => !Player.IsBreakTime.Value);
|
AddAssert("not in break", () => !Player.IsBreakTime.Value);
|
||||||
|
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.DrawableRuleset.Playfield));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkKey(Func<KeyCounter> counter, int count, bool active)
|
private void checkKey(Func<KeyCounter> counter, int count, bool active)
|
||||||
|
@ -7,21 +7,25 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Database;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Overlays.SkinEditor;
|
using osu.Game.Overlays.SkinEditor;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Skinning.Components;
|
using osu.Game.Skinning.Components;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
@ -39,6 +43,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Cached]
|
[Cached]
|
||||||
public readonly EditorClipboard Clipboard = new EditorClipboard();
|
public readonly EditorClipboard Clipboard = new EditorClipboard();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private SkinManager skins { get; set; } = null!;
|
||||||
|
|
||||||
private SkinComponentsContainer targetContainer => Player.ChildrenOfType<SkinComponentsContainer>().First();
|
private SkinComponentsContainer targetContainer => Player.ChildrenOfType<SkinComponentsContainer>().First();
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
@ -46,6 +53,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
base.SetUpSteps();
|
base.SetUpSteps();
|
||||||
|
|
||||||
|
AddStep("reset skin", () => skins.CurrentSkinInfo.SetDefault());
|
||||||
AddUntilStep("wait for hud load", () => targetContainer.ComponentsLoaded);
|
AddUntilStep("wait for hud load", () => targetContainer.ComponentsLoaded);
|
||||||
|
|
||||||
AddStep("reload skin editor", () =>
|
AddStep("reload skin editor", () =>
|
||||||
@ -369,6 +377,93 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
() => Is.EqualTo(3));
|
() => Is.EqualTo(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SkinComponentsContainer globalHUDTarget => Player.ChildrenOfType<SkinComponentsContainer>()
|
||||||
|
.Single(c => c.Lookup.Target == SkinComponentsContainerLookup.TargetArea.MainHUDComponents && c.Lookup.Ruleset == null);
|
||||||
|
|
||||||
|
private SkinComponentsContainer rulesetHUDTarget => Player.ChildrenOfType<SkinComponentsContainer>()
|
||||||
|
.Single(c => c.Lookup.Target == SkinComponentsContainerLookup.TargetArea.MainHUDComponents && c.Lookup.Ruleset != null);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMigrationArgon()
|
||||||
|
{
|
||||||
|
Live<SkinInfo> importedSkin = null!;
|
||||||
|
|
||||||
|
AddStep("import old argon skin", () => skins.CurrentSkinInfo.Value = importedSkin = importSkinFromArchives(@"argon-layout-version-0.osk").SkinInfo);
|
||||||
|
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||||
|
AddAssert("no combo in global target", () => !globalHUDTarget.Components.OfType<ArgonComboCounter>().Any());
|
||||||
|
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<ArgonComboCounter>().Count() == 1);
|
||||||
|
|
||||||
|
AddStep("add combo to global target", () => globalHUDTarget.Add(new ArgonComboCounter
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(2f),
|
||||||
|
}));
|
||||||
|
AddStep("save skin", () => skins.Save(skins.CurrentSkin.Value));
|
||||||
|
|
||||||
|
AddStep("select another skin", () => skins.CurrentSkinInfo.SetDefault());
|
||||||
|
AddStep("select skin again", () => skins.CurrentSkinInfo.Value = importedSkin);
|
||||||
|
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||||
|
AddAssert("combo placed in global target", () => globalHUDTarget.Components.OfType<ArgonComboCounter>().Count() == 1);
|
||||||
|
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<ArgonComboCounter>().Count() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMigrationTriangles()
|
||||||
|
{
|
||||||
|
Live<SkinInfo> importedSkin = null!;
|
||||||
|
|
||||||
|
AddStep("import old triangles skin", () => skins.CurrentSkinInfo.Value = importedSkin = importSkinFromArchives(@"triangles-layout-version-0.osk").SkinInfo);
|
||||||
|
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||||
|
AddAssert("no combo in global target", () => !globalHUDTarget.Components.OfType<DefaultComboCounter>().Any());
|
||||||
|
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<DefaultComboCounter>().Count() == 1);
|
||||||
|
|
||||||
|
AddStep("add combo to global target", () => globalHUDTarget.Add(new DefaultComboCounter
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(2f),
|
||||||
|
}));
|
||||||
|
AddStep("save skin", () => skins.Save(skins.CurrentSkin.Value));
|
||||||
|
|
||||||
|
AddStep("select another skin", () => skins.CurrentSkinInfo.SetDefault());
|
||||||
|
AddStep("select skin again", () => skins.CurrentSkinInfo.Value = importedSkin);
|
||||||
|
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||||
|
AddAssert("combo placed in global target", () => globalHUDTarget.Components.OfType<DefaultComboCounter>().Count() == 1);
|
||||||
|
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<DefaultComboCounter>().Count() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMigrationLegacy()
|
||||||
|
{
|
||||||
|
Live<SkinInfo> importedSkin = null!;
|
||||||
|
|
||||||
|
AddStep("import old classic skin", () => skins.CurrentSkinInfo.Value = importedSkin = importSkinFromArchives(@"classic-layout-version-0.osk").SkinInfo);
|
||||||
|
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||||
|
AddAssert("no combo in global target", () => !globalHUDTarget.Components.OfType<LegacyComboCounter>().Any());
|
||||||
|
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<LegacyComboCounter>().Count() == 1);
|
||||||
|
|
||||||
|
AddStep("add combo to global target", () => globalHUDTarget.Add(new LegacyComboCounter
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(2f),
|
||||||
|
}));
|
||||||
|
AddStep("save skin", () => skins.Save(skins.CurrentSkin.Value));
|
||||||
|
|
||||||
|
AddStep("select another skin", () => skins.CurrentSkinInfo.SetDefault());
|
||||||
|
AddStep("select skin again", () => skins.CurrentSkinInfo.Value = importedSkin);
|
||||||
|
AddUntilStep("wait for load", () => globalHUDTarget.ComponentsLoaded && rulesetHUDTarget.ComponentsLoaded);
|
||||||
|
AddAssert("combo placed in global target", () => globalHUDTarget.Components.OfType<LegacyComboCounter>().Count() == 1);
|
||||||
|
AddAssert("combo placed in ruleset target", () => rulesetHUDTarget.Components.OfType<LegacyComboCounter>().Count() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Skin importSkinFromArchives(string filename)
|
||||||
|
{
|
||||||
|
var imported = skins.Import(new ImportTask(TestResources.OpenResource($@"Archives/{filename}"), filename)).GetResultSafely();
|
||||||
|
return imported.PerformRead(skinInfo => skins.GetSkin(skinInfo));
|
||||||
|
}
|
||||||
|
|
||||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||||
|
|
||||||
private partial class TestSkinEditorChangeHandler : SkinEditorChangeHandler
|
private partial class TestSkinEditorChangeHandler : SkinEditorChangeHandler
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
@ -28,17 +27,5 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("reset combo", () => scoreProcessor.Combo.Value = 0);
|
AddStep("reset combo", () => scoreProcessor.Combo.Value = 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestLegacyComboCounterHiddenByRulesetImplementation()
|
|
||||||
{
|
|
||||||
AddToggleStep("toggle legacy hidden by ruleset", visible =>
|
|
||||||
{
|
|
||||||
foreach (var legacyCounter in this.ChildrenOfType<LegacyComboCounter>())
|
|
||||||
legacyCounter.HiddenByRulesetImplementation = visible;
|
|
||||||
});
|
|
||||||
|
|
||||||
AddRepeatStep("increase combo", () => scoreProcessor.Combo.Value++, 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,13 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Tests.Gameplay;
|
using osu.Game.Tests.Gameplay;
|
||||||
using osuTK.Input;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
@ -91,10 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
SetContents(_ =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());
|
hudOverlay = new HUDOverlay(new DrawableOsuRuleset(new OsuRuleset(), new OsuBeatmap()), Array.Empty<Mod>());
|
||||||
|
|
||||||
// Add any key just to display the key counter visually.
|
|
||||||
hudOverlay.InputCountController.Add(new KeyCounterKeyboardTrigger(Key.Space));
|
|
||||||
|
|
||||||
action?.Invoke(hudOverlay);
|
action?.Invoke(hudOverlay);
|
||||||
|
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
// 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.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public partial class TestSceneSkinnableKeyCounter : SkinnableHUDComponentTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private readonly InputCountController controller = new InputCountController();
|
||||||
|
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("create dependencies", () =>
|
||||||
|
{
|
||||||
|
Add(controller);
|
||||||
|
controller.Add(new KeyCounterKeyboardTrigger(Key.Z));
|
||||||
|
controller.Add(new KeyCounterKeyboardTrigger(Key.X));
|
||||||
|
controller.Add(new KeyCounterKeyboardTrigger(Key.C));
|
||||||
|
controller.Add(new KeyCounterKeyboardTrigger(Key.V));
|
||||||
|
|
||||||
|
foreach (var trigger in controller.Triggers)
|
||||||
|
Add(trigger);
|
||||||
|
});
|
||||||
|
base.SetUpSteps();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||||
|
|
||||||
|
protected override Drawable CreateDefaultImplementation() => new ArgonKeyCounterDisplay();
|
||||||
|
|
||||||
|
protected override Drawable CreateLegacyImplementation() => new LegacyKeyCounterDisplay();
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,15 @@
|
|||||||
// 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 NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Online.Metadata;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -23,6 +27,49 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
AddStep("disable return to top on idle", () => Game.ChildrenOfType<ButtonSystem>().Single().ReturnToTopOnIdle = false);
|
AddStep("disable return to top on idle", () => Game.ChildrenOfType<ButtonSystem>().Single().ReturnToTopOnIdle = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDailyChallenge()
|
||||||
|
{
|
||||||
|
AddStep("set up API", () => ((DummyAPIAccess)API).HandleRequest = req =>
|
||||||
|
{
|
||||||
|
switch (req)
|
||||||
|
{
|
||||||
|
case GetRoomRequest getRoomRequest:
|
||||||
|
if (getRoomRequest.RoomId != 1234)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var beatmap = CreateAPIBeatmap();
|
||||||
|
beatmap.OnlineID = 1001;
|
||||||
|
getRoomRequest.TriggerSuccess(new Room
|
||||||
|
{
|
||||||
|
RoomID = { Value = 1234 },
|
||||||
|
Name = { Value = "Aug 8, 2024" },
|
||||||
|
Playlist =
|
||||||
|
{
|
||||||
|
new PlaylistItem(beatmap)
|
||||||
|
},
|
||||||
|
StartDate = { Value = DateTimeOffset.Now.AddMinutes(-30) },
|
||||||
|
EndDate = { Value = DateTimeOffset.Now.AddSeconds(60) }
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("beatmap of the day active", () => Game.ChildrenOfType<IMetadataClient>().Single().DailyChallengeUpdated(new DailyChallengeInfo
|
||||||
|
{
|
||||||
|
RoomID = 1234,
|
||||||
|
}));
|
||||||
|
|
||||||
|
AddStep("enter menu", () => InputManager.Key(Key.P));
|
||||||
|
AddStep("enter submenu", () => InputManager.Key(Key.P));
|
||||||
|
AddStep("enter daily challenge", () => InputManager.Key(Key.D));
|
||||||
|
|
||||||
|
AddUntilStep("wait for daily challenge screen", () => Game.ScreenStack.CurrentScreen, Is.TypeOf<Screens.OnlinePlay.DailyChallenge.DailyChallenge>);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestOnlineMenuBannerTrusted()
|
public void TestOnlineMenuBannerTrusted()
|
||||||
{
|
{
|
||||||
|
@ -61,7 +61,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
AddStep("select difficulty adjust", () => freeModSelectOverlay.SelectedMods.Value = new[] { new OsuModDifficultyAdjust() });
|
AddStep("select difficulty adjust", () => freeModSelectOverlay.SelectedMods.Value = new[] { new OsuModDifficultyAdjust() });
|
||||||
AddWaitStep("wait some", 3);
|
AddWaitStep("wait some", 3);
|
||||||
AddAssert("customisation area not expanded", () => !this.ChildrenOfType<ModCustomisationPanel>().Single().Expanded.Value);
|
AddAssert("customisation area not expanded",
|
||||||
|
() => this.ChildrenOfType<ModCustomisationPanel>().Single().ExpandedState.Value,
|
||||||
|
() => Is.EqualTo(ModCustomisationPanel.ModCustomisationPanelState.Collapsed));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -139,8 +139,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
private void addRandomPlayer()
|
private void addRandomPlayer()
|
||||||
{
|
{
|
||||||
int randomUser = RNG.Next(200000, 500000);
|
int id = TestResources.GetNextTestID();
|
||||||
multiplayerClient.AddUser(new APIUser { Id = randomUser, Username = $"user {randomUser}" });
|
multiplayerClient.AddUser(new APIUser { Id = id, Username = $"user {id}" });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeLastUser()
|
private void removeLastUser()
|
||||||
|
@ -9,13 +9,13 @@ 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.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Chat;
|
using osu.Game.Online.Chat;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Chat.ChannelList;
|
using osu.Game.Overlays.Chat.ChannelList;
|
||||||
using osu.Game.Overlays.Chat.Listing;
|
using osu.Game.Overlays.Chat.Listing;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
{
|
{
|
||||||
@ -160,7 +160,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
private Channel createRandomPublicChannel()
|
private Channel createRandomPublicChannel()
|
||||||
{
|
{
|
||||||
int id = RNG.Next(0, 10000);
|
int id = TestResources.GetNextTestID();
|
||||||
return new Channel
|
return new Channel
|
||||||
{
|
{
|
||||||
Name = $"#channel-{id}",
|
Name = $"#channel-{id}",
|
||||||
@ -171,7 +171,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
private Channel createRandomPrivateChannel()
|
private Channel createRandomPrivateChannel()
|
||||||
{
|
{
|
||||||
int id = RNG.Next(0, 10000);
|
int id = TestResources.GetNextTestID();
|
||||||
return new Channel(new APIUser
|
return new Channel(new APIUser
|
||||||
{
|
{
|
||||||
Id = id,
|
Id = id,
|
||||||
@ -181,7 +181,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
private Channel createRandomAnnounceChannel()
|
private Channel createRandomAnnounceChannel()
|
||||||
{
|
{
|
||||||
int id = RNG.Next(0, 10000);
|
int id = TestResources.GetNextTestID();
|
||||||
return new Channel
|
return new Channel
|
||||||
{
|
{
|
||||||
Name = $"Announce {id}",
|
Name = $"Announce {id}",
|
||||||
|
@ -19,7 +19,6 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -33,6 +32,7 @@ using osu.Game.Overlays.Chat.ChannelList;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
{
|
{
|
||||||
@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case PostMessageRequest postMessage:
|
case PostMessageRequest postMessage:
|
||||||
postMessage.TriggerSuccess(new Message(RNG.Next(0, 10000000))
|
postMessage.TriggerSuccess(new Message(TestResources.GetNextTestID())
|
||||||
{
|
{
|
||||||
Content = postMessage.Message.Content,
|
Content = postMessage.Message.Content,
|
||||||
ChannelId = postMessage.Message.ChannelId,
|
ChannelId = postMessage.Message.ChannelId,
|
||||||
@ -719,7 +719,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
private Channel createPrivateChannel()
|
private Channel createPrivateChannel()
|
||||||
{
|
{
|
||||||
int id = RNG.Next(0, DummyAPIAccess.DUMMY_USER_ID - 1);
|
int id = TestResources.GetNextTestID();
|
||||||
|
|
||||||
return new Channel(new APIUser
|
return new Channel(new APIUser
|
||||||
{
|
{
|
||||||
Id = id,
|
Id = id,
|
||||||
|
@ -33,6 +33,34 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMention()
|
||||||
|
{
|
||||||
|
AddStep("add normal message", () => channel.AddNewMessages(
|
||||||
|
new Message(1)
|
||||||
|
{
|
||||||
|
Sender = new APIUser
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Username = "TestUser2"
|
||||||
|
},
|
||||||
|
Content = "Hello how are you today?",
|
||||||
|
Timestamp = new DateTimeOffset(2021, 12, 11, 13, 33, 24, TimeSpan.Zero)
|
||||||
|
}));
|
||||||
|
|
||||||
|
AddStep("add mention", () => channel.AddNewMessages(
|
||||||
|
new Message(2)
|
||||||
|
{
|
||||||
|
Sender = new APIUser
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Username = "TestUser2"
|
||||||
|
},
|
||||||
|
Content = $"Hello {API.LocalUser.Value.Username} how are you today?",
|
||||||
|
Timestamp = new DateTimeOffset(2021, 12, 11, 13, 33, 25, TimeSpan.Zero)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDaySeparators()
|
public void TestDaySeparators()
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Profile;
|
||||||
|
using osu.Game.Overlays.Profile.Header.Components;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public partial class TestSceneUserProfileDailyChallenge : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
public readonly Bindable<UserProfileData?> User = new Bindable<UserProfileData?>(new UserProfileData(new APIUser(), new OsuRuleset().RulesetInfo));
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
DailyChallengeStatsDisplay display = null!;
|
||||||
|
|
||||||
|
AddSliderStep("daily", 0, 999, 2, v => update(s => s.DailyStreakCurrent = v));
|
||||||
|
AddSliderStep("daily best", 0, 999, 2, v => update(s => s.DailyStreakBest = v));
|
||||||
|
AddSliderStep("weekly", 0, 250, 1, v => update(s => s.WeeklyStreakCurrent = v));
|
||||||
|
AddSliderStep("weekly best", 0, 250, 1, v => update(s => s.WeeklyStreakBest = v));
|
||||||
|
AddSliderStep("top 10%", 0, 999, 0, v => update(s => s.Top10PercentPlacements = v));
|
||||||
|
AddSliderStep("top 50%", 0, 999, 0, v => update(s => s.Top50PercentPlacements = v));
|
||||||
|
AddSliderStep("playcount", 0, 999, 0, v => update(s => s.PlayCount = v));
|
||||||
|
AddStep("create", () =>
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
Add(new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background2,
|
||||||
|
});
|
||||||
|
Add(display = new DailyChallengeStatsDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(1f),
|
||||||
|
User = { BindTarget = User },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
AddStep("hover", () => InputManager.MoveMouseTo(display));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(Action<APIUserDailyChallengeStatistics> change)
|
||||||
|
{
|
||||||
|
change.Invoke(User.Value!.User.DailyChallengeStatistics);
|
||||||
|
User.Value = new UserProfileData(User.Value.User, User.Value.Ruleset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -24,7 +25,17 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public void SetUp()
|
public void SetUp()
|
||||||
{
|
{
|
||||||
AddStep("create profile overlay", () => Child = profile = new UserProfileOverlay());
|
AddStep("create profile overlay", () =>
|
||||||
|
{
|
||||||
|
profile = new UserProfileOverlay();
|
||||||
|
|
||||||
|
Child = new DependencyProvidingContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
CachedDependencies = new (Type, object)[] { (typeof(UserProfileOverlay), profile) },
|
||||||
|
Child = profile,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -131,6 +142,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
CountryCode = CountryCode.JP,
|
CountryCode = CountryCode.JP,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
|
||||||
ProfileHue = hue,
|
ProfileHue = hue,
|
||||||
|
PlayMode = "osu",
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -174,6 +186,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
CountryCode = CountryCode.JP,
|
CountryCode = CountryCode.JP,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
|
||||||
ProfileHue = hue,
|
ProfileHue = hue,
|
||||||
|
PlayMode = "osu",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
int hue2 = 0;
|
int hue2 = 0;
|
||||||
@ -189,6 +202,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
CountryCode = CountryCode.JP,
|
CountryCode = CountryCode.JP,
|
||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
|
||||||
ProfileHue = hue2,
|
ProfileHue = hue2,
|
||||||
|
PlayMode = "osu",
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,6 +296,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
ImageUrlLowRes = "https://assets.ppy.sh/profile-badges/contributor.png",
|
ImageUrlLowRes = "https://assets.ppy.sh/profile-badges/contributor.png",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
DailyChallengeStatistics = new APIUserDailyChallengeStatistics
|
||||||
|
{
|
||||||
|
DailyStreakCurrent = 231,
|
||||||
|
WeeklyStreakCurrent = 18,
|
||||||
|
DailyStreakBest = 370,
|
||||||
|
WeeklyStreakBest = 51,
|
||||||
|
Top10PercentPlacements = 345,
|
||||||
|
Top50PercentPlacements = 427,
|
||||||
|
},
|
||||||
Title = "osu!volunteer",
|
Title = "osu!volunteer",
|
||||||
Colour = "ff0000",
|
Colour = "ff0000",
|
||||||
Achievements = Array.Empty<APIUserAchievement>(),
|
Achievements = Array.Empty<APIUserAchievement>(),
|
||||||
|
@ -135,7 +135,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
|
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() >= beforePanelCount + scores_per_result);
|
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||||
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
|
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
|
AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible);
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() >= beforePanelCount + scores_per_result);
|
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||||
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
|
AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden);
|
||||||
|
|
||||||
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType<ScorePanel>().Count());
|
||||||
@ -191,7 +191,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
AddAssert("left loading spinner shown", () => resultsScreen.LeftSpinner.State.Value == Visibility.Visible);
|
AddAssert("left loading spinner shown", () => resultsScreen.LeftSpinner.State.Value == Visibility.Visible);
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() >= beforePanelCount + scores_per_result);
|
AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType<ScorePanel>().Count() == beforePanelCount + scores_per_result);
|
||||||
AddAssert("left loading spinner hidden", () => resultsScreen.LeftSpinner.State.Value == Visibility.Hidden);
|
AddAssert("left loading spinner hidden", () => resultsScreen.LeftSpinner.State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
new PlaylistItem(beatmap)
|
new PlaylistItem(beatmap)
|
||||||
},
|
},
|
||||||
|
StartDate = { Value = DateTimeOffset.Now.AddMinutes(-5) },
|
||||||
EndDate = { Value = DateTimeOffset.Now.AddSeconds(30) }
|
EndDate = { Value = DateTimeOffset.Now.AddSeconds(30) }
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
@ -95,8 +96,13 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
AddAssert("no notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.Zero);
|
AddAssert("notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.EqualTo(1));
|
||||||
|
|
||||||
|
AddStep("clear notifications", () =>
|
||||||
|
{
|
||||||
|
foreach (var notification in notificationOverlay.AllNotifications)
|
||||||
|
notification.Close(runFlingAnimation: false);
|
||||||
|
});
|
||||||
AddStep("beatmap of the day not active", () => metadataClient.DailyChallengeUpdated(null));
|
AddStep("beatmap of the day not active", () => metadataClient.DailyChallengeUpdated(null));
|
||||||
AddAssert("no notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.Zero);
|
AddAssert("no notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.Zero);
|
||||||
|
|
||||||
@ -105,7 +111,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
RoomID = 1234,
|
RoomID = 1234,
|
||||||
}));
|
}));
|
||||||
AddAssert("notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.EqualTo(1));
|
AddAssert("no notification posted", () => notificationOverlay.AllNotifications.Count(), () => Is.Zero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -10,6 +11,8 @@ using osu.Game.Overlays;
|
|||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.UserInterface
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
{
|
{
|
||||||
@ -19,10 +22,15 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
private ModCustomisationPanel panel = null!;
|
private ModCustomisationPanel panel = null!;
|
||||||
|
private ModCustomisationHeader header = null!;
|
||||||
|
private Container content = null!;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
|
SelectedMods.Value = Array.Empty<Mod>();
|
||||||
|
InputManager.MoveMouseTo(Vector2.One);
|
||||||
|
|
||||||
Child = new Container
|
Child = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -36,6 +44,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
SelectedMods = { BindTarget = SelectedMods },
|
SelectedMods = { BindTarget = SelectedMods },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
header = panel.Children.OfType<ModCustomisationHeader>().First();
|
||||||
|
content = panel.Children.OfType<Container>().First();
|
||||||
});
|
});
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -44,23 +55,112 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("set DT", () =>
|
AddStep("set DT", () =>
|
||||||
{
|
{
|
||||||
SelectedMods.Value = new[] { new OsuModDoubleTime() };
|
SelectedMods.Value = new[] { new OsuModDoubleTime() };
|
||||||
panel.Enabled.Value = panel.Expanded.Value = true;
|
panel.Enabled.Value = true;
|
||||||
|
panel.ExpandedState.Value = ModCustomisationPanel.ModCustomisationPanelState.Expanded;
|
||||||
});
|
});
|
||||||
AddStep("set DA", () =>
|
AddStep("set DA", () =>
|
||||||
{
|
{
|
||||||
SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() };
|
SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() };
|
||||||
panel.Enabled.Value = panel.Expanded.Value = true;
|
panel.Enabled.Value = true;
|
||||||
|
panel.ExpandedState.Value = ModCustomisationPanel.ModCustomisationPanelState.Expanded;
|
||||||
});
|
});
|
||||||
AddStep("set FL+WU+DA+AD", () =>
|
AddStep("set FL+WU+DA+AD", () =>
|
||||||
{
|
{
|
||||||
SelectedMods.Value = new Mod[] { new OsuModFlashlight(), new ModWindUp(), new OsuModDifficultyAdjust(), new OsuModApproachDifferent() };
|
SelectedMods.Value = new Mod[] { new OsuModFlashlight(), new ModWindUp(), new OsuModDifficultyAdjust(), new OsuModApproachDifferent() };
|
||||||
panel.Enabled.Value = panel.Expanded.Value = true;
|
panel.Enabled.Value = true;
|
||||||
|
panel.ExpandedState.Value = ModCustomisationPanel.ModCustomisationPanelState.Expanded;
|
||||||
});
|
});
|
||||||
AddStep("set empty", () =>
|
AddStep("set empty", () =>
|
||||||
{
|
{
|
||||||
SelectedMods.Value = Array.Empty<Mod>();
|
SelectedMods.Value = Array.Empty<Mod>();
|
||||||
panel.Enabled.Value = panel.Expanded.Value = false;
|
panel.Enabled.Value = false;
|
||||||
|
panel.ExpandedState.Value = ModCustomisationPanel.ModCustomisationPanelState.Collapsed;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHoverDoesNotExpandWhenNoCustomisableMods()
|
||||||
|
{
|
||||||
|
AddStep("hover header", () => InputManager.MoveMouseTo(header));
|
||||||
|
|
||||||
|
checkExpanded(false);
|
||||||
|
|
||||||
|
AddStep("hover content", () => InputManager.MoveMouseTo(content));
|
||||||
|
|
||||||
|
checkExpanded(false);
|
||||||
|
|
||||||
|
AddStep("left from content", () => InputManager.MoveMouseTo(Vector2.One));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHoverExpandsWithCustomisableMods()
|
||||||
|
{
|
||||||
|
AddStep("add customisable mod", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = new[] { new OsuModDoubleTime() };
|
||||||
|
panel.Enabled.Value = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("hover header", () => InputManager.MoveMouseTo(header));
|
||||||
|
checkExpanded(true);
|
||||||
|
|
||||||
|
AddStep("move to content", () => InputManager.MoveMouseTo(content));
|
||||||
|
checkExpanded(true);
|
||||||
|
|
||||||
|
AddStep("move away", () => InputManager.MoveMouseTo(Vector2.One));
|
||||||
|
checkExpanded(false);
|
||||||
|
|
||||||
|
AddStep("hover header", () => InputManager.MoveMouseTo(header));
|
||||||
|
checkExpanded(true);
|
||||||
|
|
||||||
|
AddStep("move away", () => InputManager.MoveMouseTo(Vector2.One));
|
||||||
|
checkExpanded(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestExpandedStatePersistsWhenClicked()
|
||||||
|
{
|
||||||
|
AddStep("add customisable mod", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = new[] { new OsuModDoubleTime() };
|
||||||
|
panel.Enabled.Value = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("hover header", () => InputManager.MoveMouseTo(header));
|
||||||
|
checkExpanded(true);
|
||||||
|
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
checkExpanded(false);
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
checkExpanded(true);
|
||||||
|
|
||||||
|
AddStep("move away", () => InputManager.MoveMouseTo(Vector2.One));
|
||||||
|
checkExpanded(true);
|
||||||
|
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
checkExpanded(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHoverExpandsAndCollapsesWhenHeaderClicked()
|
||||||
|
{
|
||||||
|
AddStep("add customisable mod", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = new[] { new OsuModDoubleTime() };
|
||||||
|
panel.Enabled.Value = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("hover header", () => InputManager.MoveMouseTo(header));
|
||||||
|
checkExpanded(true);
|
||||||
|
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
checkExpanded(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkExpanded(bool expanded)
|
||||||
|
{
|
||||||
|
AddUntilStep(expanded ? "is expanded" : "not expanded", () => panel.ExpandedState.Value,
|
||||||
|
() => expanded ? Is.Not.EqualTo(ModCustomisationPanel.ModCustomisationPanelState.Collapsed) : Is.EqualTo(ModCustomisationPanel.ModCustomisationPanelState.Collapsed));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("reset ruleset", () => Ruleset.Value = rulesetStore.GetRuleset(0));
|
AddStep("reset ruleset", () => Ruleset.Value = rulesetStore.GetRuleset(0));
|
||||||
AddStep("reset mods", () => SelectedMods.SetDefault());
|
AddStep("reset mods", () => SelectedMods.SetDefault());
|
||||||
AddStep("reset config", () => configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, true));
|
AddStep("reset config", () => configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, true));
|
||||||
|
AddStep("reset mouse", () => InputManager.MoveMouseTo(Vector2.One));
|
||||||
AddStep("set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo));
|
AddStep("set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo));
|
||||||
AddStep("set up presets", () =>
|
AddStep("set up presets", () =>
|
||||||
{
|
{
|
||||||
@ -998,7 +999,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("press mouse", () => InputManager.PressButton(MouseButton.Left));
|
AddStep("press mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||||
AddAssert("search still not focused", () => !this.ChildrenOfType<ShearedSearchTextBox>().Single().HasFocus);
|
AddAssert("search still not focused", () => !this.ChildrenOfType<ShearedSearchTextBox>().Single().HasFocus);
|
||||||
AddStep("release mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
AddStep("release mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
AddAssert("customisation panel closed by click", () => !this.ChildrenOfType<ModCustomisationPanel>().Single().Expanded.Value);
|
AddAssert("customisation panel closed by click",
|
||||||
|
() => this.ChildrenOfType<ModCustomisationPanel>().Single().ExpandedState.Value,
|
||||||
|
() => Is.EqualTo(ModCustomisationPanel.ModCustomisationPanelState.Collapsed));
|
||||||
|
|
||||||
if (textSearchStartsActive)
|
if (textSearchStartsActive)
|
||||||
AddAssert("search focused", () => this.ChildrenOfType<ShearedSearchTextBox>().Single().HasFocus);
|
AddAssert("search focused", () => this.ChildrenOfType<ShearedSearchTextBox>().Single().HasFocus);
|
||||||
@ -1021,7 +1024,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
private void assertCustomisationToggleState(bool disabled, bool active)
|
private void assertCustomisationToggleState(bool disabled, bool active)
|
||||||
{
|
{
|
||||||
AddUntilStep($"customisation panel is {(disabled ? "" : "not ")}disabled", () => modSelectOverlay.ChildrenOfType<ModCustomisationPanel>().Single().Enabled.Value == !disabled);
|
AddUntilStep($"customisation panel is {(disabled ? "" : "not ")}disabled", () => modSelectOverlay.ChildrenOfType<ModCustomisationPanel>().Single().Enabled.Value == !disabled);
|
||||||
AddAssert($"customisation panel is {(active ? "" : "not ")}active", () => modSelectOverlay.ChildrenOfType<ModCustomisationPanel>().Single().Expanded.Value == active);
|
AddAssert($"customisation panel is {(active ? "" : "not ")}active",
|
||||||
|
() => modSelectOverlay.ChildrenOfType<ModCustomisationPanel>().Single().ExpandedState.Value,
|
||||||
|
() => active ? Is.Not.EqualTo(ModCustomisationPanel.ModCustomisationPanelState.Collapsed) : Is.EqualTo(ModCustomisationPanel.ModCustomisationPanelState.Collapsed));
|
||||||
}
|
}
|
||||||
|
|
||||||
private T getSelectedMod<T>() where T : Mod => SelectedMods.Value.OfType<T>().Single();
|
private T getSelectedMod<T>() where T : Mod => SelectedMods.Value.OfType<T>().Single();
|
||||||
|
86
osu.Game.Tests/Visual/UserInterface/TestSceneOsuTooltip.cs
Normal file
86
osu.Game.Tests/Visual/UserInterface/TestSceneOsuTooltip.cs
Normal 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;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Cursor;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public partial class TestSceneOsuTooltip : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private TestTooltipContainer container = null!;
|
||||||
|
|
||||||
|
private static readonly string[] test_case_tooltip_string =
|
||||||
|
[
|
||||||
|
"Hello!!",
|
||||||
|
string.Concat(Enumerable.Repeat("Hello ", 100)),
|
||||||
|
|
||||||
|
//TODO: o!f issue: https://github.com/ppy/osu-framework/issues/5007
|
||||||
|
//Enable after o!f fixed
|
||||||
|
// $"H{new string('e', 500)}llo",
|
||||||
|
];
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding(100),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = Colour4.Red.Opacity(0.5f),
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
container = new TestTooltipContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = "Hover me!",
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Font = OsuFont.GetFont(size: 50)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(test_case_tooltip_string))]
|
||||||
|
public void TestTooltipBasic(string text)
|
||||||
|
{
|
||||||
|
AddStep("Set tooltip content", () => container.TooltipText = text);
|
||||||
|
|
||||||
|
AddStep("Move mouse to container", () => InputManager.MoveMouseTo(new Vector2(InputManager.ScreenSpaceDrawQuad.Centre.X, InputManager.ScreenSpaceDrawQuad.Centre.Y)));
|
||||||
|
|
||||||
|
OsuTooltipContainer.OsuTooltip? tooltip = null!;
|
||||||
|
|
||||||
|
AddUntilStep("Wait for the tooltip shown", () =>
|
||||||
|
{
|
||||||
|
tooltip = container.FindClosestParent<OsuTooltipContainer>().ChildrenOfType<OsuTooltipContainer.OsuTooltip>().FirstOrDefault();
|
||||||
|
return tooltip != null && tooltip.Alpha == 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("Check tooltip is under width limit", () => tooltip != null && tooltip.Width <= 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed partial class TestTooltipContainer : Container, IHasTooltip
|
||||||
|
{
|
||||||
|
public LocalisableString TooltipText { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,8 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public override async Task<Live<BeatmapSetInfo>?> ImportAsUpdate(ProgressNotification notification, ImportTask importTask, BeatmapSetInfo original)
|
public override async Task<Live<BeatmapSetInfo>?> ImportAsUpdate(ProgressNotification notification, ImportTask importTask, BeatmapSetInfo original)
|
||||||
{
|
{
|
||||||
|
var originalDateAdded = original.DateAdded;
|
||||||
|
|
||||||
Guid originalId = original.ID;
|
Guid originalId = original.ID;
|
||||||
|
|
||||||
var imported = await Import(notification, new[] { importTask }).ConfigureAwait(false);
|
var imported = await Import(notification, new[] { importTask }).ConfigureAwait(false);
|
||||||
@ -57,8 +59,11 @@ namespace osu.Game.Beatmaps
|
|||||||
// If there were no changes, ensure we don't accidentally nuke ourselves.
|
// If there were no changes, ensure we don't accidentally nuke ourselves.
|
||||||
if (first.ID == originalId)
|
if (first.ID == originalId)
|
||||||
{
|
{
|
||||||
first.PerformRead(s =>
|
first.PerformWrite(s =>
|
||||||
{
|
{
|
||||||
|
// Transfer local values which should be persisted across a beatmap update.
|
||||||
|
s.DateAdded = originalDateAdded;
|
||||||
|
|
||||||
// Re-run processing even in this case. We might have outdated metadata.
|
// Re-run processing even in this case. We might have outdated metadata.
|
||||||
ProcessBeatmap?.Invoke(s, MetadataLookupScope.OnlineFirst);
|
ProcessBeatmap?.Invoke(s, MetadataLookupScope.OnlineFirst);
|
||||||
});
|
});
|
||||||
@ -79,7 +84,7 @@ namespace osu.Game.Beatmaps
|
|||||||
original.DeletePending = true;
|
original.DeletePending = true;
|
||||||
|
|
||||||
// Transfer local values which should be persisted across a beatmap update.
|
// Transfer local values which should be persisted across a beatmap update.
|
||||||
updated.DateAdded = original.DateAdded;
|
updated.DateAdded = originalDateAdded;
|
||||||
|
|
||||||
transferCollectionReferences(realm, original, updated);
|
transferCollectionReferences(realm, original, updated);
|
||||||
|
|
||||||
@ -278,6 +283,9 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
protected override void UndeleteForReuse(BeatmapSetInfo existing)
|
protected override void UndeleteForReuse(BeatmapSetInfo existing)
|
||||||
{
|
{
|
||||||
|
if (!existing.DeletePending)
|
||||||
|
return;
|
||||||
|
|
||||||
base.UndeleteForReuse(existing);
|
base.UndeleteForReuse(existing);
|
||||||
existing.DateAdded = DateTimeOffset.UtcNow;
|
existing.DateAdded = DateTimeOffset.UtcNow;
|
||||||
}
|
}
|
||||||
|
@ -563,7 +563,7 @@ namespace osu.Game.Beatmaps
|
|||||||
remove => workingBeatmapCache.OnInvalidated -= value;
|
remove => workingBeatmapCache.OnInvalidated -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsAvailableLocally(BeatmapSetInfo model) => Realm.Run(realm => realm.All<BeatmapSetInfo>().Any(s => s.OnlineID == model.OnlineID));
|
public override bool IsAvailableLocally(BeatmapSetInfo model) => Realm.Run(realm => realm.All<BeatmapSetInfo>().Any(s => s.OnlineID == model.OnlineID && !s.DeletePending));
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -18,6 +18,12 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
public const int LATEST_VERSION = 14;
|
public const int LATEST_VERSION = 14;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The .osu format (beatmap) version.
|
||||||
|
///
|
||||||
|
/// osu!stable's versions end at <see cref="LATEST_VERSION"/>.
|
||||||
|
/// osu!lazer's versions starts at <see cref="LegacyBeatmapEncoder.FIRST_LAZER_VERSION"/>.
|
||||||
|
/// </summary>
|
||||||
protected readonly int FormatVersion;
|
protected readonly int FormatVersion;
|
||||||
|
|
||||||
protected LegacyDecoder(int version)
|
protected LegacyDecoder(int version)
|
||||||
@ -93,14 +99,8 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void HandleColours<TModel>(TModel output, string line, bool allowAlpha)
|
private Color4 convertSettingStringToColor4(string[] split, bool allowAlpha, KeyValuePair<string, string> pair)
|
||||||
{
|
{
|
||||||
var pair = SplitKeyVal(line);
|
|
||||||
|
|
||||||
bool isCombo = pair.Key.StartsWith(@"Combo", StringComparison.Ordinal);
|
|
||||||
|
|
||||||
string[] split = pair.Value.Split(',');
|
|
||||||
|
|
||||||
if (split.Length != 3 && split.Length != 4)
|
if (split.Length != 3 && split.Length != 4)
|
||||||
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B or R,G,B,A): {pair.Value}");
|
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B or R,G,B,A): {pair.Value}");
|
||||||
|
|
||||||
@ -116,6 +116,18 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
|
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void HandleColours<TModel>(TModel output, string line, bool allowAlpha)
|
||||||
|
{
|
||||||
|
var pair = SplitKeyVal(line);
|
||||||
|
|
||||||
|
string[] split = pair.Value.Split(',');
|
||||||
|
Color4 colour = convertSettingStringToColor4(split, allowAlpha, pair);
|
||||||
|
|
||||||
|
bool isCombo = pair.Key.StartsWith(@"Combo", StringComparison.Ordinal);
|
||||||
|
|
||||||
if (isCombo)
|
if (isCombo)
|
||||||
{
|
{
|
||||||
if (!(output is IHasComboColours tHasComboColours)) return;
|
if (!(output is IHasComboColours tHasComboColours)) return;
|
||||||
|
@ -43,6 +43,9 @@ namespace osu.Game.Collections
|
|||||||
//
|
//
|
||||||
// if we want to support user sorting (but changes will need to be made to realm to persist).
|
// if we want to support user sorting (but changes will need to be made to realm to persist).
|
||||||
ShowDragHandle.Value = false;
|
ShowDragHandle.Value = false;
|
||||||
|
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = item_height / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Drawable CreateContent() => new ItemContent(Model);
|
protected override Drawable CreateContent() => new ItemContent(Model);
|
||||||
@ -50,7 +53,7 @@ namespace osu.Game.Collections
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The main content of the <see cref="DrawableCollectionListItem"/>.
|
/// The main content of the <see cref="DrawableCollectionListItem"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private partial class ItemContent : CircularContainer
|
private partial class ItemContent : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly Live<BeatmapCollection> collection;
|
private readonly Live<BeatmapCollection> collection;
|
||||||
|
|
||||||
@ -65,13 +68,12 @@ namespace osu.Game.Collections
|
|||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = item_height;
|
Height = item_height;
|
||||||
Masking = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
collection.IsManaged
|
collection.IsManaged
|
||||||
? new DeleteButton(collection)
|
? new DeleteButton(collection)
|
||||||
@ -132,7 +134,7 @@ namespace osu.Game.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class DeleteButton : CompositeDrawable
|
public partial class DeleteButton : OsuClickableContainer
|
||||||
{
|
{
|
||||||
public Func<Vector2, bool> IsTextBoxHovered = null!;
|
public Func<Vector2, bool> IsTextBoxHovered = null!;
|
||||||
|
|
||||||
@ -155,7 +157,7 @@ namespace osu.Game.Collections
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
InternalChild = fadeContainer = new Container
|
Child = fadeContainer = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0.1f,
|
Alpha = 0.1f,
|
||||||
@ -176,6 +178,14 @@ namespace osu.Game.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Action = () =>
|
||||||
|
{
|
||||||
|
if (collection.PerformRead(c => c.BeatmapMD5Hashes.Count) == 0)
|
||||||
|
deleteCollection();
|
||||||
|
else
|
||||||
|
dialogOverlay?.Push(new DeleteCollectionDialog(collection, deleteCollection));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) && !IsTextBoxHovered(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) && !IsTextBoxHovered(screenSpacePos);
|
||||||
@ -195,12 +205,7 @@ namespace osu.Game.Collections
|
|||||||
{
|
{
|
||||||
background.FlashColour(Color4.White, 150);
|
background.FlashColour(Color4.White, 150);
|
||||||
|
|
||||||
if (collection.PerformRead(c => c.BeatmapMD5Hashes.Count) == 0)
|
return base.OnClick(e);
|
||||||
deleteCollection();
|
|
||||||
else
|
|
||||||
dialogOverlay?.Push(new DeleteCollectionDialog(collection, deleteCollection));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteCollection() => collection.PerformWrite(c => c.Realm!.Remove(c));
|
private void deleteCollection() => collection.PerformWrite(c => c.Realm!.Remove(c));
|
||||||
|
@ -67,13 +67,7 @@ namespace osu.Game.Configuration
|
|||||||
SetDefault(OsuSetting.Username, string.Empty);
|
SetDefault(OsuSetting.Username, string.Empty);
|
||||||
SetDefault(OsuSetting.Token, string.Empty);
|
SetDefault(OsuSetting.Token, string.Empty);
|
||||||
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
SetDefault(OsuSetting.AutomaticallyDownloadMissingBeatmaps, true);
|
||||||
// this default set MUST remain despite the setting being deprecated, because `SetDefault()` calls are implicitly used to declare the type returned for the lookup.
|
|
||||||
// if this is removed, the setting will be interpreted as a string, and `Migrate()` will fail due to cast failure.
|
|
||||||
// can be removed 20240618
|
|
||||||
SetDefault(OsuSetting.AutomaticallyDownloadWhenSpectating, false);
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
SetDefault(OsuSetting.AutomaticallyDownloadMissingBeatmaps, false);
|
|
||||||
|
|
||||||
SetDefault(OsuSetting.SavePassword, true).ValueChanged += enabled =>
|
SetDefault(OsuSetting.SavePassword, true).ValueChanged += enabled =>
|
||||||
{
|
{
|
||||||
@ -244,12 +238,6 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
// migrations can be added here using a condition like:
|
// migrations can be added here using a condition like:
|
||||||
// if (combined < 20220103) { performMigration() }
|
// if (combined < 20220103) { performMigration() }
|
||||||
if (combined < 20230918)
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
|
||||||
SetValue(OsuSetting.AutomaticallyDownloadMissingBeatmaps, Get<bool>(OsuSetting.AutomaticallyDownloadWhenSpectating)); // can be removed 20240618
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override TrackedSettings CreateTrackedSettings()
|
public override TrackedSettings CreateTrackedSettings()
|
||||||
@ -424,9 +412,6 @@ namespace osu.Game.Configuration
|
|||||||
EditorAutoSeekOnPlacement,
|
EditorAutoSeekOnPlacement,
|
||||||
DiscordRichPresence,
|
DiscordRichPresence,
|
||||||
|
|
||||||
[Obsolete($"Use {nameof(AutomaticallyDownloadMissingBeatmaps)} instead.")] // can be removed 20240318
|
|
||||||
AutomaticallyDownloadWhenSpectating,
|
|
||||||
|
|
||||||
ShowOnlineExplicitContent,
|
ShowOnlineExplicitContent,
|
||||||
LastProcessedMetadataId,
|
LastProcessedMetadataId,
|
||||||
SafeAreaConsiderations,
|
SafeAreaConsiderations,
|
||||||
|
@ -64,6 +64,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new HoverClickSounds(),
|
||||||
new GridContainer
|
new GridContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
@ -92,7 +93,6 @@ namespace osu.Game.Graphics.Containers
|
|||||||
ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
|
ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
|
||||||
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }
|
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }
|
||||||
},
|
},
|
||||||
new HoverClickSounds()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,16 +8,24 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
|
|
||||||
namespace osu.Game.Graphics.Cursor
|
namespace osu.Game.Graphics.Cursor
|
||||||
{
|
{
|
||||||
|
[Cached(typeof(OsuContextMenuContainer))]
|
||||||
public partial class OsuContextMenuContainer : ContextMenuContainer
|
public partial class OsuContextMenuContainer : ContextMenuContainer
|
||||||
{
|
{
|
||||||
[Cached]
|
[Cached]
|
||||||
private OsuContextMenuSamples samples = new OsuContextMenuSamples();
|
private OsuContextMenuSamples samples = new OsuContextMenuSamples();
|
||||||
|
|
||||||
|
private OsuContextMenu menu = null!;
|
||||||
|
|
||||||
public OsuContextMenuContainer()
|
public OsuContextMenuContainer()
|
||||||
{
|
{
|
||||||
AddInternal(samples);
|
AddInternal(samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Menu CreateMenu() => new OsuContextMenu(true);
|
protected override Menu CreateMenu() => menu = new OsuContextMenu(true);
|
||||||
|
|
||||||
|
public void CloseMenu()
|
||||||
|
{
|
||||||
|
menu.Close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ using osu.Framework.Graphics.Cursor;
|
|||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Cursor
|
namespace osu.Game.Graphics.Cursor
|
||||||
{
|
{
|
||||||
@ -27,15 +27,20 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
|
|
||||||
public partial class OsuTooltip : Tooltip
|
public partial class OsuTooltip : Tooltip
|
||||||
{
|
{
|
||||||
|
private const float max_width = 500;
|
||||||
|
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
private readonly OsuSpriteText text;
|
private readonly TextFlowContainer text;
|
||||||
private bool instantMovement = true;
|
private bool instantMovement = true;
|
||||||
|
|
||||||
public override void SetContent(LocalisableString contentString)
|
private LocalisableString lastContent;
|
||||||
{
|
|
||||||
if (contentString == text.Text) return;
|
|
||||||
|
|
||||||
text.Text = contentString;
|
public override void SetContent(LocalisableString content)
|
||||||
|
{
|
||||||
|
if (content.Equals(lastContent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
text.Text = content;
|
||||||
|
|
||||||
if (IsPresent)
|
if (IsPresent)
|
||||||
{
|
{
|
||||||
@ -44,6 +49,8 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
AutoSizeDuration = 0;
|
AutoSizeDuration = 0;
|
||||||
|
|
||||||
|
lastContent = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsuTooltip()
|
public OsuTooltip()
|
||||||
@ -65,10 +72,14 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0.9f,
|
Alpha = 0.9f,
|
||||||
},
|
},
|
||||||
text = new OsuSpriteText
|
text = new TextFlowContainer(f =>
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding(5),
|
f.Font = OsuFont.GetFont(weight: FontWeight.Regular);
|
||||||
Font = OsuFont.GetFont(weight: FontWeight.Regular)
|
})
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding(5),
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
MaximumSize = new Vector2(max_width, float.PositiveInfinity),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString MentionUser => new TranslatableString(getKey(@"mention_user"), @"Mention");
|
public static LocalisableString MentionUser => new TranslatableString(getKey(@"mention_user"), @"Mention");
|
||||||
|
|
||||||
private static string getKey(string key) => $"{prefix}:{key}";
|
/// <summary>
|
||||||
|
/// "press enter to chat..."
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString InGameInputPlaceholder => new TranslatableString(getKey(@"in_game_input_placeholder"), @"press enter to chat...");
|
||||||
|
|
||||||
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,16 @@ namespace osu.Game.Localisation.HUD
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString ShowGraphDescription => new TranslatableString(getKey(@"show_graph_description"), "Whether a graph displaying difficulty throughout the beatmap should be shown");
|
public static LocalisableString ShowGraphDescription => new TranslatableString(getKey(@"show_graph_description"), "Whether a graph displaying difficulty throughout the beatmap should be shown");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Show time"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ShowTime => new TranslatableString(getKey(@"show_time"), "Show time");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Whether the passed and remaining time should be shown"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ShowTimeDescription => new TranslatableString(getKey(@"show_time_description"), "Whether the passed and remaining time should be shown");
|
||||||
|
|
||||||
private static string getKey(string key) => $"{prefix}:{key}";
|
private static string getKey(string key) => $"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,6 +272,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
[JsonProperty("groups")]
|
[JsonProperty("groups")]
|
||||||
public APIUserGroup[] Groups;
|
public APIUserGroup[] Groups;
|
||||||
|
|
||||||
|
[JsonProperty("daily_challenge_user_stats")]
|
||||||
|
public APIUserDailyChallengeStatistics DailyChallengeStatistics = new APIUserDailyChallengeStatistics();
|
||||||
|
|
||||||
public override string ToString() => Username;
|
public override string ToString() => Username;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -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 System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests.Responses
|
||||||
|
{
|
||||||
|
public class APIUserDailyChallengeStatistics
|
||||||
|
{
|
||||||
|
[JsonProperty("user_id")]
|
||||||
|
public int UserID;
|
||||||
|
|
||||||
|
[JsonProperty("daily_streak_best")]
|
||||||
|
public int DailyStreakBest;
|
||||||
|
|
||||||
|
[JsonProperty("daily_streak_current")]
|
||||||
|
public int DailyStreakCurrent;
|
||||||
|
|
||||||
|
[JsonProperty("weekly_streak_best")]
|
||||||
|
public int WeeklyStreakBest;
|
||||||
|
|
||||||
|
[JsonProperty("weekly_streak_current")]
|
||||||
|
public int WeeklyStreakCurrent;
|
||||||
|
|
||||||
|
[JsonProperty("top_10p_placements")]
|
||||||
|
public int Top10PercentPlacements;
|
||||||
|
|
||||||
|
[JsonProperty("top_50p_placements")]
|
||||||
|
public int Top50PercentPlacements;
|
||||||
|
|
||||||
|
[JsonProperty("playcount")]
|
||||||
|
public int PlayCount;
|
||||||
|
|
||||||
|
[JsonProperty("last_update")]
|
||||||
|
public DateTimeOffset? LastUpdate;
|
||||||
|
|
||||||
|
[JsonProperty("last_weekly_streak")]
|
||||||
|
public DateTimeOffset? LastWeeklyStreak;
|
||||||
|
}
|
||||||
|
}
|
@ -128,6 +128,9 @@ namespace osu.Game.Online.Chat
|
|||||||
|
|
||||||
public partial class ChatTextBox : HistoryTextBox
|
public partial class ChatTextBox : HistoryTextBox
|
||||||
{
|
{
|
||||||
|
public Action Focus;
|
||||||
|
public Action FocusLost;
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
{
|
{
|
||||||
// Chat text boxes are generally used in places where they retain focus, but shouldn't block interaction with other
|
// Chat text boxes are generally used in places where they retain focus, but shouldn't block interaction with other
|
||||||
@ -153,13 +156,17 @@ namespace osu.Game.Online.Chat
|
|||||||
BackgroundFocused = new Color4(10, 10, 10, 255);
|
BackgroundFocused = new Color4(10, 10, 10, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnFocus(FocusEvent e)
|
||||||
|
{
|
||||||
|
base.OnFocus(e);
|
||||||
|
Focus?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnFocusLost(FocusLostEvent e)
|
protected override void OnFocusLost(FocusLostEvent e)
|
||||||
{
|
{
|
||||||
base.OnFocusLost(e);
|
base.OnFocusLost(e);
|
||||||
FocusLost?.Invoke();
|
FocusLost?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Action FocusLost;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class StandAloneDrawableChannel : DrawableChannel
|
public partial class StandAloneDrawableChannel : DrawableChannel
|
||||||
|
@ -782,7 +782,21 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new AggregateException($"Item: {JsonConvert.SerializeObject(createPlaylistItem(item))}\n\nRoom:{JsonConvert.SerializeObject(APIRoom)}", ex);
|
// Temporary code to attempt to figure out long-term failing tests.
|
||||||
|
bool success = true;
|
||||||
|
int indexOf = -1234;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
indexOf = Room.Playlist.IndexOf(Room.Playlist.Single(existing => existing.ID == item.ID));
|
||||||
|
Room.Playlist[indexOf] = item;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AggregateException($"Index: {indexOf} Length: {Room.Playlist.Count} Retry success: {success} Item: {JsonConvert.SerializeObject(createPlaylistItem(item))}\n\nRoom:{JsonConvert.SerializeObject(APIRoom)}", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemChanged?.Invoke(item);
|
ItemChanged?.Invoke(item);
|
||||||
|
@ -642,10 +642,10 @@ namespace osu.Game
|
|||||||
Live<BeatmapSetInfo> databasedSet = null;
|
Live<BeatmapSetInfo> databasedSet = null;
|
||||||
|
|
||||||
if (beatmap.OnlineID > 0)
|
if (beatmap.OnlineID > 0)
|
||||||
databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineID == beatmap.OnlineID);
|
databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineID == beatmap.OnlineID && !s.DeletePending);
|
||||||
|
|
||||||
if (beatmap is BeatmapSetInfo localBeatmap)
|
if (beatmap is BeatmapSetInfo localBeatmap)
|
||||||
databasedSet ??= BeatmapManager.QueryBeatmapSet(s => s.Hash == localBeatmap.Hash);
|
databasedSet ??= BeatmapManager.QueryBeatmapSet(s => s.Hash == localBeatmap.Hash && !s.DeletePending);
|
||||||
|
|
||||||
if (databasedSet == null)
|
if (databasedSet == null)
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,7 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Chat;
|
using osu.Game.Online.Chat;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -104,6 +105,8 @@ namespace osu.Game.Overlays.Chat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool isMention;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The colour used to paint the author's username.
|
/// The colour used to paint the author's username.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -255,12 +258,21 @@ namespace osu.Game.Overlays.Chat
|
|||||||
private void styleMessageContent(SpriteText text)
|
private void styleMessageContent(SpriteText text)
|
||||||
{
|
{
|
||||||
text.Shadow = false;
|
text.Shadow = false;
|
||||||
text.Font = text.Font.With(size: FontSize, italics: Message.IsAction);
|
text.Font = text.Font.With(size: FontSize, italics: Message.IsAction, weight: isMention ? FontWeight.SemiBold : FontWeight.Medium);
|
||||||
|
|
||||||
bool messageHasColour = Message.IsAction && !string.IsNullOrEmpty(message.Sender.Colour);
|
Color4 messageColour = colourProvider?.Content1 ?? Colour4.White;
|
||||||
text.Colour = messageHasColour ? Color4Extensions.FromHex(message.Sender.Colour) : colourProvider?.Content1 ?? Colour4.White;
|
|
||||||
|
if (isMention)
|
||||||
|
messageColour = colourProvider?.Highlight1 ?? Color4.Orange;
|
||||||
|
else if (Message.IsAction && !string.IsNullOrEmpty(message.Sender.Colour))
|
||||||
|
messageColour = Color4Extensions.FromHex(message.Sender.Colour);
|
||||||
|
|
||||||
|
text.Colour = messageColour;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; } = null!;
|
||||||
|
|
||||||
private void updateMessageContent()
|
private void updateMessageContent()
|
||||||
{
|
{
|
||||||
this.FadeTo(message is LocalEchoMessage ? 0.4f : 1.0f, 500, Easing.OutQuint);
|
this.FadeTo(message is LocalEchoMessage ? 0.4f : 1.0f, 500, Easing.OutQuint);
|
||||||
@ -280,6 +292,8 @@ namespace osu.Game.Overlays.Chat
|
|||||||
// remove non-existent channels from the link list
|
// remove non-existent channels from the link list
|
||||||
message.Links.RemoveAll(link => link.Action == LinkAction.OpenChannel && chatManager?.AvailableChannels.Any(c => c.Name == link.Argument.ToString()) != true);
|
message.Links.RemoveAll(link => link.Action == LinkAction.OpenChannel && chatManager?.AvailableChannels.Any(c => c.Name == link.Argument.ToString()) != true);
|
||||||
|
|
||||||
|
isMention = MessageNotifier.CheckContainsUsername(message.DisplayContent, api.LocalUser.Value.Username);
|
||||||
|
|
||||||
drawableContentFlow.Clear();
|
drawableContentFlow.Clear();
|
||||||
drawableContentFlow.AddLinks(message.DisplayContent, message.Links);
|
drawableContentFlow.AddLinks(message.DisplayContent, message.Links);
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,14 @@ 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.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
using static osu.Game.Overlays.Mods.ModCustomisationPanel;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Mods
|
namespace osu.Game.Overlays.Mods
|
||||||
{
|
{
|
||||||
@ -29,11 +31,13 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
protected override IEnumerable<Drawable> EffectTargets => new[] { background };
|
protected override IEnumerable<Drawable> EffectTargets => new[] { background };
|
||||||
|
|
||||||
public readonly BindableBool Expanded = new BindableBool();
|
public readonly Bindable<ModCustomisationPanelState> ExpandedState = new Bindable<ModCustomisationPanelState>(ModCustomisationPanelState.Collapsed);
|
||||||
|
|
||||||
public ModCustomisationHeader()
|
private readonly ModCustomisationPanel panel;
|
||||||
|
|
||||||
|
public ModCustomisationHeader(ModCustomisationPanel panel)
|
||||||
{
|
{
|
||||||
Action = Expanded.Toggle;
|
this.panel = panel;
|
||||||
Enabled.Value = false;
|
Enabled.Value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +58,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.White.Opacity(0.4f),
|
Colour = Color4.White.Opacity(0.4f),
|
||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
|
Alpha = 0,
|
||||||
},
|
},
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
@ -101,10 +106,48 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
Expanded.BindValueChanged(v =>
|
ExpandedState.BindValueChanged(v =>
|
||||||
{
|
{
|
||||||
icon.ScaleTo(v.NewValue ? new Vector2(1, -1) : Vector2.One, 300, Easing.OutQuint);
|
icon.ScaleTo(v.NewValue > ModCustomisationPanelState.Collapsed ? new Vector2(1, -1) : Vector2.One, 300, Easing.OutQuint);
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e)
|
||||||
|
{
|
||||||
|
if (Enabled.Value)
|
||||||
|
{
|
||||||
|
ExpandedState.Value = ExpandedState.Value switch
|
||||||
|
{
|
||||||
|
ModCustomisationPanelState.Collapsed => ModCustomisationPanelState.Expanded,
|
||||||
|
_ => ModCustomisationPanelState.Collapsed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnClick(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool touchedThisFrame;
|
||||||
|
|
||||||
|
protected override bool OnTouchDown(TouchDownEvent e)
|
||||||
|
{
|
||||||
|
if (Enabled.Value)
|
||||||
|
{
|
||||||
|
touchedThisFrame = true;
|
||||||
|
Schedule(() => touchedThisFrame = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnTouchDown(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
if (Enabled.Value)
|
||||||
|
{
|
||||||
|
if (!touchedThisFrame && panel.ExpandedState.Value == ModCustomisationPanelState.Collapsed)
|
||||||
|
panel.ExpandedState.Value = ModCustomisationPanelState.ExpandedByHover;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
public readonly BindableBool Enabled = new BindableBool();
|
public readonly BindableBool Enabled = new BindableBool();
|
||||||
|
|
||||||
public readonly BindableBool Expanded = new BindableBool();
|
public readonly Bindable<ModCustomisationPanelState> ExpandedState = new Bindable<ModCustomisationPanelState>();
|
||||||
|
|
||||||
public Bindable<IReadOnlyList<Mod>> SelectedMods { get; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
public Bindable<IReadOnlyList<Mod>> SelectedMods { get; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||||
|
|
||||||
@ -46,9 +46,9 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
// Handle{Non}PositionalInput controls whether the panel should act as a blocking layer on the screen. only block when the panel is expanded.
|
// Handle{Non}PositionalInput controls whether the panel should act as a blocking layer on the screen. only block when the panel is expanded.
|
||||||
// These properties are used because they correctly handle blocking/unblocking hover when mouse is pointing at a drawable outside
|
// These properties are used because they correctly handle blocking/unblocking hover when mouse is pointing at a drawable outside
|
||||||
// (returning Expanded.Value to OnHover or overriding Block{Non}PositionalInput doesn't work).
|
// (handling OnHover or overriding Block{Non}PositionalInput doesn't work).
|
||||||
public override bool HandlePositionalInput => Expanded.Value;
|
public override bool HandlePositionalInput => ExpandedState.Value != ModCustomisationPanelState.Collapsed;
|
||||||
public override bool HandleNonPositionalInput => Expanded.Value;
|
public override bool HandleNonPositionalInput => ExpandedState.Value != ModCustomisationPanelState.Collapsed;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
@ -57,15 +57,15 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
new ModCustomisationHeader
|
new ModCustomisationHeader(this)
|
||||||
{
|
{
|
||||||
Depth = float.MinValue,
|
Depth = float.MinValue,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = header_height,
|
Height = header_height,
|
||||||
Enabled = { BindTarget = Enabled },
|
Enabled = { BindTarget = Enabled },
|
||||||
Expanded = { BindTarget = Expanded },
|
ExpandedState = { BindTarget = ExpandedState },
|
||||||
},
|
},
|
||||||
content = new FocusGrabbingContainer
|
content = new FocusGrabbingContainer(this)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
BorderColour = colourProvider.Dark3,
|
BorderColour = colourProvider.Dark3,
|
||||||
@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Roundness = 5f,
|
Roundness = 5f,
|
||||||
Colour = Color4.Black.Opacity(0.25f),
|
Colour = Color4.Black.Opacity(0.25f),
|
||||||
},
|
},
|
||||||
Expanded = { BindTarget = Expanded },
|
ExpandedState = { BindTarget = ExpandedState },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
@ -122,7 +122,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
this.FadeColour(OsuColour.Gray(e.NewValue ? 1f : 0.6f), 300, Easing.OutQuint);
|
this.FadeColour(OsuColour.Gray(e.NewValue ? 1f : 0.6f), 300, Easing.OutQuint);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
Expanded.BindValueChanged(_ => updateDisplay(), true);
|
ExpandedState.BindValueChanged(_ => updateDisplay(), true);
|
||||||
SelectedMods.BindValueChanged(_ => updateMods(), true);
|
SelectedMods.BindValueChanged(_ => updateMods(), true);
|
||||||
|
|
||||||
FinishTransforms(true);
|
FinishTransforms(true);
|
||||||
@ -134,7 +134,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
Expanded.Value = false;
|
ExpandedState.Value = ModCustomisationPanelState.Collapsed;
|
||||||
return base.OnClick(e);
|
return base.OnClick(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
case GlobalAction.Back:
|
case GlobalAction.Back:
|
||||||
Expanded.Value = false;
|
ExpandedState.Value = ModCustomisationPanelState.Collapsed;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
content.ClearTransforms();
|
content.ClearTransforms();
|
||||||
|
|
||||||
if (Expanded.Value)
|
if (ExpandedState.Value != ModCustomisationPanelState.Collapsed)
|
||||||
{
|
{
|
||||||
content.AutoSizeDuration = 400;
|
content.AutoSizeDuration = 400;
|
||||||
content.AutoSizeEasing = Easing.OutQuint;
|
content.AutoSizeEasing = Easing.OutQuint;
|
||||||
@ -179,7 +179,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private void updateMods()
|
private void updateMods()
|
||||||
{
|
{
|
||||||
Expanded.Value = false;
|
ExpandedState.Value = ModCustomisationPanelState.Collapsed;
|
||||||
sectionsFlow.Clear();
|
sectionsFlow.Clear();
|
||||||
|
|
||||||
// Importantly, the selected mods bindable is already ordered by the mod select overlay (following the order of mod columns and panels).
|
// Importantly, the selected mods bindable is already ordered by the mod select overlay (following the order of mod columns and panels).
|
||||||
@ -202,10 +202,35 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private partial class FocusGrabbingContainer : InputBlockingContainer
|
private partial class FocusGrabbingContainer : InputBlockingContainer
|
||||||
{
|
{
|
||||||
public IBindable<bool> Expanded { get; } = new BindableBool();
|
public readonly Bindable<ModCustomisationPanelState> ExpandedState = new Bindable<ModCustomisationPanelState>();
|
||||||
|
|
||||||
public override bool RequestsFocus => Expanded.Value;
|
public override bool RequestsFocus => panel.ExpandedState.Value != ModCustomisationPanelState.Collapsed;
|
||||||
public override bool AcceptsFocus => Expanded.Value;
|
public override bool AcceptsFocus => panel.ExpandedState.Value != ModCustomisationPanelState.Collapsed;
|
||||||
|
|
||||||
|
private readonly ModCustomisationPanel panel;
|
||||||
|
|
||||||
|
public FocusGrabbingContainer(ModCustomisationPanel panel)
|
||||||
|
{
|
||||||
|
this.panel = panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
if (ExpandedState.Value is ModCustomisationPanelState.ExpandedByHover
|
||||||
|
&& !ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
|
||||||
|
{
|
||||||
|
ExpandedState.Value = ModCustomisationPanelState.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ModCustomisationPanelState
|
||||||
|
{
|
||||||
|
Collapsed = 0,
|
||||||
|
ExpandedByHover = 1,
|
||||||
|
Expanded = 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
},
|
},
|
||||||
new GridContainer
|
new GridContainer
|
||||||
{
|
{
|
||||||
|
Padding = new MarginPadding { Top = 1, Bottom = 3 },
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RowDimensions = new[]
|
RowDimensions = new[]
|
||||||
{
|
{
|
||||||
|
@ -237,7 +237,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
ActiveMods.Value = ComputeActiveMods();
|
ActiveMods.Value = ComputeActiveMods();
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
customisationPanel.Expanded.BindValueChanged(_ => updateCustomisationVisualState(), true);
|
customisationPanel.ExpandedState.BindValueChanged(_ => updateCustomisationVisualState(), true);
|
||||||
|
|
||||||
SearchTextBox.Current.BindValueChanged(query =>
|
SearchTextBox.Current.BindValueChanged(query =>
|
||||||
{
|
{
|
||||||
@ -368,18 +368,18 @@ namespace osu.Game.Overlays.Mods
|
|||||||
customisationPanel.Enabled.Value = true;
|
customisationPanel.Enabled.Value = true;
|
||||||
|
|
||||||
if (anyModPendingConfiguration)
|
if (anyModPendingConfiguration)
|
||||||
customisationPanel.Expanded.Value = true;
|
customisationPanel.ExpandedState.Value = ModCustomisationPanel.ModCustomisationPanelState.Expanded;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
customisationPanel.Expanded.Value = false;
|
customisationPanel.ExpandedState.Value = ModCustomisationPanel.ModCustomisationPanelState.Collapsed;
|
||||||
customisationPanel.Enabled.Value = false;
|
customisationPanel.Enabled.Value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCustomisationVisualState()
|
private void updateCustomisationVisualState()
|
||||||
{
|
{
|
||||||
if (customisationPanel.Expanded.Value)
|
if (customisationPanel.ExpandedState.Value != ModCustomisationPanel.ModCustomisationPanelState.Collapsed)
|
||||||
{
|
{
|
||||||
columnScroll.FadeColour(OsuColour.Gray(0.5f), 400, Easing.OutQuint);
|
columnScroll.FadeColour(OsuColour.Gray(0.5f), 400, Easing.OutQuint);
|
||||||
SearchTextBox.FadeColour(OsuColour.Gray(0.5f), 400, Easing.OutQuint);
|
SearchTextBox.FadeColour(OsuColour.Gray(0.5f), 400, Easing.OutQuint);
|
||||||
@ -544,7 +544,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
nonFilteredColumnCount += 1;
|
nonFilteredColumnCount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
customisationPanel.Expanded.Value = false;
|
customisationPanel.ExpandedState.Value = ModCustomisationPanel.ModCustomisationPanelState.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -571,7 +571,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
// wherein activating the binding will both change the contents of the search text box and deselect all mods.
|
// wherein activating the binding will both change the contents of the search text box and deselect all mods.
|
||||||
case GlobalAction.DeselectAllMods:
|
case GlobalAction.DeselectAllMods:
|
||||||
{
|
{
|
||||||
if (!SearchTextBox.HasFocus && !customisationPanel.Expanded.Value)
|
if (!SearchTextBox.HasFocus && customisationPanel.ExpandedState.Value == ModCustomisationPanel.ModCustomisationPanelState.Collapsed)
|
||||||
{
|
{
|
||||||
DisplayedFooterContent?.DeselectAllModsButton?.TriggerClick();
|
DisplayedFooterContent?.DeselectAllModsButton?.TriggerClick();
|
||||||
return true;
|
return true;
|
||||||
@ -637,7 +637,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
if (e.Repeat || e.Key != Key.Tab)
|
if (e.Repeat || e.Key != Key.Tab)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (customisationPanel.Expanded.Value)
|
if (customisationPanel.ExpandedState.Value != ModCustomisationPanel.ModCustomisationPanelState.Collapsed)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// TODO: should probably eventually support typical platform search shortcuts (`Ctrl-F`, `/`)
|
// TODO: should probably eventually support typical platform search shortcuts (`Ctrl-F`, `/`)
|
||||||
@ -668,6 +668,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
[Cached]
|
[Cached]
|
||||||
internal partial class ColumnScrollContainer : OsuScrollContainer<ColumnFlowContainer>
|
internal partial class ColumnScrollContainer : OsuScrollContainer<ColumnFlowContainer>
|
||||||
{
|
{
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||||
|
|
||||||
public ColumnScrollContainer()
|
public ColumnScrollContainer()
|
||||||
: base(Direction.Horizontal)
|
: base(Direction.Horizontal)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
|
{
|
||||||
|
public partial class DailyChallengeStatsDisplay : CompositeDrawable, IHasCustomTooltip<DailyChallengeTooltipData>
|
||||||
|
{
|
||||||
|
public readonly Bindable<UserProfileData?> User = new Bindable<UserProfileData?>();
|
||||||
|
|
||||||
|
public DailyChallengeTooltipData? TooltipContent { get; private set; }
|
||||||
|
|
||||||
|
private OsuSpriteText dailyPlayCount = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
CornerRadius = 5;
|
||||||
|
Masking = true;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background4,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Padding = new MarginPadding(5f),
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12))
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
// can't use this because osu-web does weird stuff with \\n.
|
||||||
|
// Text = UsersStrings.ShowDailyChallengeTitle.,
|
||||||
|
Text = "Daily\nChallenge",
|
||||||
|
Margin = new MarginPadding { Horizontal = 5f, Bottom = 2f },
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
CornerRadius = 5f,
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background6,
|
||||||
|
},
|
||||||
|
dailyPlayCount = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
UseFullGlyphHeight = false,
|
||||||
|
Colour = colourProvider.Content2,
|
||||||
|
Margin = new MarginPadding { Horizontal = 10f, Vertical = 5f },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
User.BindValueChanged(_ => updateDisplay(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDisplay()
|
||||||
|
{
|
||||||
|
if (User.Value == null || User.Value.Ruleset.OnlineID != 0)
|
||||||
|
{
|
||||||
|
Hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
APIUserDailyChallengeStatistics stats = User.Value.User.DailyChallengeStatistics;
|
||||||
|
|
||||||
|
dailyPlayCount.Text = UsersStrings.ShowDailyChallengeUnitDay(stats.PlayCount.ToLocalisableString("N0"));
|
||||||
|
dailyPlayCount.Colour = colours.ForRankingTier(tierForPlayCount(stats.PlayCount));
|
||||||
|
|
||||||
|
TooltipContent = new DailyChallengeTooltipData(colourProvider, stats);
|
||||||
|
|
||||||
|
Show();
|
||||||
|
|
||||||
|
static RankingTier tierForPlayCount(int playCount) => DailyChallengeStatsTooltip.TierForDaily(playCount / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITooltip<DailyChallengeTooltipData> GetCustomTooltip() => new DailyChallengeStatsTooltip();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,241 @@
|
|||||||
|
// 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.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
using Box = osu.Framework.Graphics.Shapes.Box;
|
||||||
|
using Color4 = osuTK.Graphics.Color4;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
|
{
|
||||||
|
public partial class DailyChallengeStatsTooltip : VisibilityContainer, ITooltip<DailyChallengeTooltipData>
|
||||||
|
{
|
||||||
|
private StreakPiece currentDaily = null!;
|
||||||
|
private StreakPiece currentWeekly = null!;
|
||||||
|
private StatisticsPiece bestDaily = null!;
|
||||||
|
private StatisticsPiece bestWeekly = null!;
|
||||||
|
private StatisticsPiece topTen = null!;
|
||||||
|
private StatisticsPiece topFifty = null!;
|
||||||
|
|
||||||
|
private Box topBackground = null!;
|
||||||
|
private Box background = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; } = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
CornerRadius = 20f;
|
||||||
|
Masking = true;
|
||||||
|
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Colour = Color4.Black.Opacity(0.25f),
|
||||||
|
Radius = 30f,
|
||||||
|
};
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
topBackground = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Padding = new MarginPadding(15f),
|
||||||
|
Spacing = new Vector2(30f),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
currentDaily = new StreakPiece(UsersStrings.ShowDailyChallengeDailyStreakCurrent),
|
||||||
|
currentWeekly = new StreakPiece(UsersStrings.ShowDailyChallengeWeeklyStreakCurrent),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding(15f),
|
||||||
|
Spacing = new Vector2(10f),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
bestDaily = new StatisticsPiece(UsersStrings.ShowDailyChallengeDailyStreakBest),
|
||||||
|
bestWeekly = new StatisticsPiece(UsersStrings.ShowDailyChallengeWeeklyStreakBest),
|
||||||
|
topTen = new StatisticsPiece(UsersStrings.ShowDailyChallengeTop10pPlacements),
|
||||||
|
topFifty = new StatisticsPiece(UsersStrings.ShowDailyChallengeTop50pPlacements),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetContent(DailyChallengeTooltipData content)
|
||||||
|
{
|
||||||
|
var statistics = content.Statistics;
|
||||||
|
var colourProvider = content.ColourProvider;
|
||||||
|
|
||||||
|
background.Colour = colourProvider.Background4;
|
||||||
|
topBackground.Colour = colourProvider.Background5;
|
||||||
|
|
||||||
|
currentDaily.Value = UsersStrings.ShowDailyChallengeUnitDay(content.Statistics.DailyStreakCurrent.ToLocalisableString(@"N0"));
|
||||||
|
currentDaily.ValueColour = colours.ForRankingTier(TierForDaily(statistics.DailyStreakCurrent));
|
||||||
|
|
||||||
|
currentWeekly.Value = UsersStrings.ShowDailyChallengeUnitWeek(statistics.WeeklyStreakCurrent.ToLocalisableString(@"N0"));
|
||||||
|
currentWeekly.ValueColour = colours.ForRankingTier(TierForWeekly(statistics.WeeklyStreakCurrent));
|
||||||
|
|
||||||
|
bestDaily.Value = UsersStrings.ShowDailyChallengeUnitDay(statistics.DailyStreakBest.ToLocalisableString(@"N0"));
|
||||||
|
bestDaily.ValueColour = colours.ForRankingTier(TierForDaily(statistics.DailyStreakBest));
|
||||||
|
|
||||||
|
bestWeekly.Value = UsersStrings.ShowDailyChallengeUnitWeek(statistics.WeeklyStreakBest.ToLocalisableString(@"N0"));
|
||||||
|
bestWeekly.ValueColour = colours.ForRankingTier(TierForWeekly(statistics.WeeklyStreakBest));
|
||||||
|
|
||||||
|
topTen.Value = statistics.Top10PercentPlacements.ToLocalisableString(@"N0");
|
||||||
|
topTen.ValueColour = colourProvider.Content2;
|
||||||
|
|
||||||
|
topFifty.Value = statistics.Top50PercentPlacements.ToLocalisableString(@"N0");
|
||||||
|
topFifty.ValueColour = colourProvider.Content2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reference: https://github.com/ppy/osu-web/blob/8206e0e91eeea80ccf92f0586561346dd40e085e/resources/js/profile-page/daily-challenge.tsx#L13-L43
|
||||||
|
public static RankingTier TierForDaily(int daily)
|
||||||
|
{
|
||||||
|
if (daily > 360)
|
||||||
|
return RankingTier.Lustrous;
|
||||||
|
|
||||||
|
if (daily > 240)
|
||||||
|
return RankingTier.Radiant;
|
||||||
|
|
||||||
|
if (daily > 120)
|
||||||
|
return RankingTier.Rhodium;
|
||||||
|
|
||||||
|
if (daily > 60)
|
||||||
|
return RankingTier.Platinum;
|
||||||
|
|
||||||
|
if (daily > 30)
|
||||||
|
return RankingTier.Gold;
|
||||||
|
|
||||||
|
if (daily > 10)
|
||||||
|
return RankingTier.Silver;
|
||||||
|
|
||||||
|
if (daily > 5)
|
||||||
|
return RankingTier.Bronze;
|
||||||
|
|
||||||
|
return RankingTier.Iron;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RankingTier TierForWeekly(int weekly) => TierForDaily((weekly - 1) * 7);
|
||||||
|
|
||||||
|
protected override void PopIn() => this.FadeIn(200, Easing.OutQuint);
|
||||||
|
|
||||||
|
protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
|
||||||
|
|
||||||
|
public void Move(Vector2 pos) => Position = pos;
|
||||||
|
|
||||||
|
private partial class StreakPiece : FillFlowContainer
|
||||||
|
{
|
||||||
|
private readonly OsuSpriteText valueText;
|
||||||
|
|
||||||
|
public LocalisableString Value
|
||||||
|
{
|
||||||
|
set => valueText.Text = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColourInfo ValueColour
|
||||||
|
{
|
||||||
|
set => valueText.Colour = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreakPiece(LocalisableString title)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
Direction = FillDirection.Vertical;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(size: 12),
|
||||||
|
Text = title,
|
||||||
|
},
|
||||||
|
valueText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(size: 40, weight: FontWeight.Light),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class StatisticsPiece : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly OsuSpriteText valueText;
|
||||||
|
|
||||||
|
public LocalisableString Value
|
||||||
|
{
|
||||||
|
set => valueText.Text = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColourInfo ValueColour
|
||||||
|
{
|
||||||
|
set => valueText.Colour = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatisticsPiece(LocalisableString title)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(size: 12),
|
||||||
|
Text = title,
|
||||||
|
},
|
||||||
|
valueText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Font = OsuFont.GetFont(size: 12),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record DailyChallengeTooltipData(OverlayColourProvider ColourProvider, APIUserDailyChallengeStatistics Statistics);
|
||||||
|
}
|
@ -44,22 +44,41 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
Spacing = new Vector2(0, 15),
|
Spacing = new Vector2(0, 15),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
new GridContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Horizontal,
|
ColumnDimensions = new[]
|
||||||
Spacing = new Vector2(20),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
detailGlobalRank = new ProfileValueDisplay(true)
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
new Dimension(GridSizeMode.Absolute, 20),
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
},
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new[]
|
||||||
{
|
{
|
||||||
Title = UsersStrings.ShowRankGlobalSimple,
|
detailGlobalRank = new ProfileValueDisplay(true)
|
||||||
},
|
{
|
||||||
detailCountryRank = new ProfileValueDisplay(true)
|
Title = UsersStrings.ShowRankGlobalSimple,
|
||||||
{
|
},
|
||||||
Title = UsersStrings.ShowRankCountrySimple,
|
Empty(),
|
||||||
},
|
detailCountryRank = new ProfileValueDisplay(true)
|
||||||
|
{
|
||||||
|
Title = UsersStrings.ShowRankCountrySimple,
|
||||||
|
},
|
||||||
|
new DailyChallengeStatsDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
User = { BindTarget = User },
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
|
@ -2,7 +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 System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
@ -10,6 +14,8 @@ namespace osu.Game.Overlays.Settings
|
|||||||
public partial class SettingsEnumDropdown<T> : SettingsDropdown<T>
|
public partial class SettingsEnumDropdown<T> : SettingsDropdown<T>
|
||||||
where T : struct, Enum
|
where T : struct, Enum
|
||||||
{
|
{
|
||||||
|
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(Control.Items.Select(i => i.GetLocalisableDescription()));
|
||||||
|
|
||||||
protected override OsuDropdown<T> CreateDropdown() => new DropdownControl();
|
protected override OsuDropdown<T> CreateDropdown() => new DropdownControl();
|
||||||
|
|
||||||
protected new partial class DropdownControl : OsuEnumDropdown<T>
|
protected new partial class DropdownControl : OsuEnumDropdown<T>
|
||||||
|
54
osu.Game/Overlays/Volume/MasterVolumeMeter.cs
Normal file
54
osu.Game/Overlays/Volume/MasterVolumeMeter.cs
Normal 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Volume
|
||||||
|
{
|
||||||
|
public partial class MasterVolumeMeter : VolumeMeter
|
||||||
|
{
|
||||||
|
private MuteButton muteButton = null!;
|
||||||
|
|
||||||
|
public Bindable<bool> IsMuted { get; } = new Bindable<bool>();
|
||||||
|
|
||||||
|
private readonly BindableDouble muteAdjustment = new BindableDouble();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private VolumeOverlay volumeOverlay { get; set; } = null!;
|
||||||
|
|
||||||
|
public MasterVolumeMeter(string name, float circleSize, Color4 meterColour)
|
||||||
|
: base(name, circleSize, meterColour)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(AudioManager audio)
|
||||||
|
{
|
||||||
|
IsMuted.BindValueChanged(muted =>
|
||||||
|
{
|
||||||
|
if (muted.NewValue)
|
||||||
|
audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment);
|
||||||
|
else
|
||||||
|
audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment);
|
||||||
|
});
|
||||||
|
|
||||||
|
Add(muteButton = new MuteButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
X = CircleSize / 2,
|
||||||
|
Y = CircleSize * 0.23f,
|
||||||
|
Current = { BindTarget = IsMuted }
|
||||||
|
});
|
||||||
|
|
||||||
|
muteButton.Current.ValueChanged += _ => volumeOverlay.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToggleMute() => muteButton.Current.Value = !muteButton.Current.Value;
|
||||||
|
}
|
||||||
|
}
|
@ -7,13 +7,13 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Volume
|
namespace osu.Game.Overlays.Volume
|
||||||
{
|
{
|
||||||
@ -33,18 +33,18 @@ namespace osu.Game.Overlays.Volume
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 hoveredColour, unhoveredColour;
|
private ColourInfo hoveredBorderColour;
|
||||||
|
private ColourInfo unhoveredBorderColour;
|
||||||
private const float width = 100;
|
private CompositeDrawable border = null!;
|
||||||
public const float HEIGHT = 35;
|
|
||||||
|
|
||||||
public MuteButton()
|
public MuteButton()
|
||||||
{
|
{
|
||||||
Content.BorderThickness = 3;
|
const float width = 30;
|
||||||
Content.CornerRadius = HEIGHT / 2;
|
const float height = 30;
|
||||||
Content.CornerExponent = 2;
|
|
||||||
|
|
||||||
Size = new Vector2(width, HEIGHT);
|
Size = new Vector2(width, height);
|
||||||
|
Content.CornerRadius = height / 2;
|
||||||
|
Content.CornerExponent = 2;
|
||||||
|
|
||||||
Action = () => Current.Value = !Current.Value;
|
Action = () => Current.Value = !Current.Value;
|
||||||
}
|
}
|
||||||
@ -52,10 +52,9 @@ namespace osu.Game.Overlays.Volume
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
hoveredColour = colours.YellowDark;
|
|
||||||
|
|
||||||
Content.BorderColour = unhoveredColour = colours.Gray1;
|
|
||||||
BackgroundColour = colours.Gray1;
|
BackgroundColour = colours.Gray1;
|
||||||
|
hoveredBorderColour = colours.PinkLight;
|
||||||
|
unhoveredBorderColour = colours.Gray1;
|
||||||
|
|
||||||
SpriteIcon icon;
|
SpriteIcon icon;
|
||||||
|
|
||||||
@ -65,26 +64,39 @@ namespace osu.Game.Overlays.Volume
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
},
|
||||||
|
border = new CircularContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
BorderThickness = 3,
|
||||||
|
BorderColour = unhoveredBorderColour,
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
AlwaysPresent = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Current.BindValueChanged(muted =>
|
Current.BindValueChanged(muted =>
|
||||||
{
|
{
|
||||||
icon.Icon = muted.NewValue ? FontAwesome.Solid.VolumeMute : FontAwesome.Solid.VolumeUp;
|
icon.Icon = muted.NewValue ? FontAwesome.Solid.VolumeMute : FontAwesome.Solid.VolumeUp;
|
||||||
icon.Size = new Vector2(muted.NewValue ? 18 : 20);
|
icon.Size = new Vector2(muted.NewValue ? 12 : 16);
|
||||||
icon.Margin = new MarginPadding { Right = muted.NewValue ? 2 : 0 };
|
icon.Margin = new MarginPadding { Right = muted.NewValue ? 2 : 0 };
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
Content.TransformTo<Container<Drawable>, ColourInfo>("BorderColour", hoveredColour, 500, Easing.OutQuint);
|
border.TransformTo(nameof(BorderColour), hoveredBorderColour, 500, Easing.OutQuint);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnHoverLost(HoverLostEvent e)
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
{
|
{
|
||||||
Content.TransformTo<Container<Drawable>, ColourInfo>("BorderColour", unhoveredColour, 500, Easing.OutQuint);
|
border.TransformTo(nameof(BorderColour), unhoveredBorderColour, 500, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
@ -35,8 +35,12 @@ namespace osu.Game.Overlays.Volume
|
|||||||
private CircularProgress volumeCircle;
|
private CircularProgress volumeCircle;
|
||||||
private CircularProgress volumeCircleGlow;
|
private CircularProgress volumeCircleGlow;
|
||||||
|
|
||||||
|
protected static readonly Vector2 LABEL_SIZE = new Vector2(120, 20);
|
||||||
|
|
||||||
public BindableDouble Bindable { get; } = new BindableDouble { MinValue = 0, MaxValue = 1, Precision = 0.01 };
|
public BindableDouble Bindable { get; } = new BindableDouble { MinValue = 0, MaxValue = 1, Precision = 0.01 };
|
||||||
private readonly float circleSize;
|
|
||||||
|
protected readonly float CircleSize;
|
||||||
|
|
||||||
private readonly Color4 meterColour;
|
private readonly Color4 meterColour;
|
||||||
private readonly string name;
|
private readonly string name;
|
||||||
|
|
||||||
@ -73,7 +77,7 @@ namespace osu.Game.Overlays.Volume
|
|||||||
|
|
||||||
public VolumeMeter(string name, float circleSize, Color4 meterColour)
|
public VolumeMeter(string name, float circleSize, Color4 meterColour)
|
||||||
{
|
{
|
||||||
this.circleSize = circleSize;
|
CircleSize = circleSize;
|
||||||
this.meterColour = meterColour;
|
this.meterColour = meterColour;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
@ -101,7 +105,7 @@ namespace osu.Game.Overlays.Volume
|
|||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Size = new Vector2(circleSize),
|
Size = new Vector2(CircleSize),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new BufferedContainer
|
new BufferedContainer
|
||||||
@ -199,7 +203,7 @@ namespace osu.Game.Overlays.Volume
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Font = OsuFont.Numeric.With(size: 0.16f * circleSize)
|
Font = OsuFont.Numeric.With(size: 0.16f * CircleSize)
|
||||||
}).WithEffect(new GlowEffect
|
}).WithEffect(new GlowEffect
|
||||||
{
|
{
|
||||||
Colour = Color4.Transparent,
|
Colour = Color4.Transparent,
|
||||||
@ -209,10 +213,10 @@ namespace osu.Game.Overlays.Volume
|
|||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Size = new Vector2(120, 20),
|
Size = LABEL_SIZE,
|
||||||
CornerRadius = 10,
|
CornerRadius = 10,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
Margin = new MarginPadding { Left = circleSize + 10 },
|
Margin = new MarginPadding { Left = CircleSize + 10 },
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
|
@ -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.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -20,21 +21,19 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
public partial class VolumeOverlay : VisibilityContainer
|
public partial class VolumeOverlay : VisibilityContainer
|
||||||
{
|
{
|
||||||
|
public Bindable<bool> IsMuted { get; } = new Bindable<bool>();
|
||||||
|
|
||||||
private const float offset = 10;
|
private const float offset = 10;
|
||||||
|
|
||||||
private VolumeMeter volumeMeterMaster = null!;
|
private VolumeMeter volumeMeterMaster = null!;
|
||||||
private VolumeMeter volumeMeterEffect = null!;
|
private VolumeMeter volumeMeterEffect = null!;
|
||||||
private VolumeMeter volumeMeterMusic = null!;
|
private VolumeMeter volumeMeterMusic = null!;
|
||||||
private MuteButton muteButton = null!;
|
|
||||||
|
|
||||||
private SelectionCycleFillFlowContainer<VolumeMeter> volumeMeters = null!;
|
private SelectionCycleFillFlowContainer<VolumeMeter> volumeMeters = null!;
|
||||||
|
|
||||||
private readonly BindableDouble muteAdjustment = new BindableDouble();
|
|
||||||
|
|
||||||
public Bindable<bool> IsMuted { get; } = new Bindable<bool>();
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, OsuColour colours)
|
private void load(AudioManager audio, OsuColour colours)
|
||||||
{
|
{
|
||||||
@ -49,14 +48,7 @@ namespace osu.Game.Overlays
|
|||||||
Width = 300,
|
Width = 300,
|
||||||
Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.75f), Color4.Black.Opacity(0))
|
Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.75f), Color4.Black.Opacity(0))
|
||||||
},
|
},
|
||||||
muteButton = new MuteButton
|
new FillFlowContainer
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Margin = new MarginPadding(10),
|
|
||||||
Current = { BindTarget = IsMuted }
|
|
||||||
},
|
|
||||||
volumeMeters = new SelectionCycleFillFlowContainer<VolumeMeter>
|
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
@ -64,26 +56,29 @@ namespace osu.Game.Overlays
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Spacing = new Vector2(0, offset),
|
Spacing = new Vector2(0, offset),
|
||||||
Margin = new MarginPadding { Left = offset },
|
Margin = new MarginPadding { Left = offset },
|
||||||
Children = new[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker),
|
volumeMeters = new SelectionCycleFillFlowContainer<VolumeMeter>
|
||||||
volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker),
|
{
|
||||||
volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker),
|
Direction = FillDirection.Vertical,
|
||||||
}
|
AutoSizeAxes = Axes.Both,
|
||||||
}
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Spacing = new Vector2(0, offset),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker),
|
||||||
|
volumeMeterMaster = new MasterVolumeMeter("MASTER", 150, colours.PinkDarker) { IsMuted = { BindTarget = IsMuted }, },
|
||||||
|
volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
volumeMeterMaster.Bindable.BindTo(audio.Volume);
|
volumeMeterMaster.Bindable.BindTo(audio.Volume);
|
||||||
volumeMeterEffect.Bindable.BindTo(audio.VolumeSample);
|
volumeMeterEffect.Bindable.BindTo(audio.VolumeSample);
|
||||||
volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack);
|
volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack);
|
||||||
|
|
||||||
IsMuted.BindValueChanged(muted =>
|
|
||||||
{
|
|
||||||
if (muted.NewValue)
|
|
||||||
audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment);
|
|
||||||
else
|
|
||||||
audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -92,8 +87,6 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
foreach (var volumeMeter in volumeMeters)
|
foreach (var volumeMeter in volumeMeters)
|
||||||
volumeMeter.Bindable.ValueChanged += _ => Show();
|
volumeMeter.Bindable.ValueChanged += _ => Show();
|
||||||
|
|
||||||
muteButton.Current.ValueChanged += _ => Show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Adjust(GlobalAction action, float amount = 1, bool isPrecise = false)
|
public bool Adjust(GlobalAction action, float amount = 1, bool isPrecise = false)
|
||||||
@ -130,7 +123,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
case GlobalAction.ToggleMute:
|
case GlobalAction.ToggleMute:
|
||||||
Show();
|
Show();
|
||||||
muteButton.Current.Value = !muteButton.Current.Value;
|
volumeMeters.OfType<MasterVolumeMeter>().First().ToggleMute();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,9 +209,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
case MouseButtonEvent mouse:
|
case MouseButtonEvent mouse:
|
||||||
// placement blueprints should generally block mouse from reaching underlying components (ie. performing clicks on interface buttons).
|
// placement blueprints should generally block mouse from reaching underlying components (ie. performing clicks on interface buttons).
|
||||||
// for now, the one exception we want to allow is when using a non-main mouse button when shift is pressed, which is used to trigger object deletion
|
return mouse.Button == MouseButton.Left || PlacementActive == PlacementState.Active;
|
||||||
// while in placement mode.
|
|
||||||
return mouse.Button == MouseButton.Left || !mouse.ShiftPressed;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
protected readonly double Offset;
|
protected readonly double Offset;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The beatmap version.
|
/// The .osu format (beatmap) version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly int FormatVersion;
|
protected readonly int FormatVersion;
|
||||||
|
|
||||||
@ -48,7 +48,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
{
|
{
|
||||||
string[] split = text.Split(',');
|
string[] split = text.Split(',');
|
||||||
|
|
||||||
Vector2 pos = new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE));
|
Vector2 pos =
|
||||||
|
FormatVersion >= LegacyBeatmapEncoder.FIRST_LAZER_VERSION
|
||||||
|
? new Vector2(Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE))
|
||||||
|
: new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE));
|
||||||
|
|
||||||
double startTime = Parsing.ParseDouble(split[2]) + Offset;
|
double startTime = Parsing.ParseDouble(split[2]) + Offset;
|
||||||
|
|
||||||
|
@ -17,13 +17,12 @@ namespace osu.Game.Screens
|
|||||||
|
|
||||||
private const float x_movement_amount = 50;
|
private const float x_movement_amount = 50;
|
||||||
|
|
||||||
private readonly bool animateOnEnter;
|
|
||||||
|
|
||||||
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
|
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
|
||||||
|
|
||||||
protected BackgroundScreen(bool animateOnEnter = true)
|
public bool AnimateEntry { get; set; } = true;
|
||||||
|
|
||||||
|
protected BackgroundScreen()
|
||||||
{
|
{
|
||||||
this.animateOnEnter = animateOnEnter;
|
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
}
|
}
|
||||||
@ -53,12 +52,11 @@ namespace osu.Game.Screens
|
|||||||
|
|
||||||
public override void OnEntering(ScreenTransitionEvent e)
|
public override void OnEntering(ScreenTransitionEvent e)
|
||||||
{
|
{
|
||||||
if (animateOnEnter)
|
if (AnimateEntry)
|
||||||
{
|
{
|
||||||
this.FadeOut();
|
this.FadeOut();
|
||||||
this.MoveToX(x_movement_amount);
|
|
||||||
|
|
||||||
this.FadeIn(TRANSITION_LENGTH, Easing.InOutQuart);
|
this.FadeIn(TRANSITION_LENGTH, Easing.InOutQuart);
|
||||||
|
this.MoveToX(x_movement_amount);
|
||||||
this.MoveToX(0, TRANSITION_LENGTH, Easing.InOutQuart);
|
this.MoveToX(0, TRANSITION_LENGTH, Easing.InOutQuart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,10 +27,14 @@ namespace osu.Game.Screens
|
|||||||
if (screen == null)
|
if (screen == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (EqualityComparer<BackgroundScreen>.Default.Equals((BackgroundScreen)CurrentScreen, screen))
|
bool isFirstScreen = CurrentScreen == null;
|
||||||
|
screen.AnimateEntry = !isFirstScreen;
|
||||||
|
|
||||||
|
if (EqualityComparer<BackgroundScreen>.Default.Equals((BackgroundScreen?)CurrentScreen, screen))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
base.Push(screen);
|
base.Push(screen);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,11 +42,6 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
|
|
||||||
protected virtual bool AllowStoryboardBackground => true;
|
protected virtual bool AllowStoryboardBackground => true;
|
||||||
|
|
||||||
public BackgroundScreenDefault(bool animateOnEnter = true)
|
|
||||||
: base(animateOnEnter)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IAPIProvider api, SkinManager skinManager, OsuConfigManager config)
|
private void load(IAPIProvider api, SkinManager skinManager, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
|
@ -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.Framework.Extensions.TypeExtensions;
|
using osu.Framework.Extensions.TypeExtensions;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
@ -16,6 +17,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
EditorBeatmap.SelectedHitObjects.CollectionChanged += (_, _) => updateInspectorText();
|
EditorBeatmap.SelectedHitObjects.CollectionChanged += (_, _) => updateInspectorText();
|
||||||
|
EditorBeatmap.PlacementObject.BindValueChanged(_ => updateInspectorText());
|
||||||
EditorBeatmap.TransactionBegan += updateInspectorText;
|
EditorBeatmap.TransactionBegan += updateInspectorText;
|
||||||
EditorBeatmap.TransactionEnded += updateInspectorText;
|
EditorBeatmap.TransactionEnded += updateInspectorText;
|
||||||
updateInspectorText();
|
updateInspectorText();
|
||||||
@ -29,24 +31,33 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
rollingTextUpdate?.Cancel();
|
rollingTextUpdate?.Cancel();
|
||||||
rollingTextUpdate = null;
|
rollingTextUpdate = null;
|
||||||
|
|
||||||
AddInspectorValues();
|
HitObject[] objects;
|
||||||
|
|
||||||
|
if (EditorBeatmap.SelectedHitObjects.Count > 0)
|
||||||
|
objects = EditorBeatmap.SelectedHitObjects.ToArray();
|
||||||
|
else if (EditorBeatmap.PlacementObject.Value != null)
|
||||||
|
objects = new[] { EditorBeatmap.PlacementObject.Value };
|
||||||
|
else
|
||||||
|
objects = Array.Empty<HitObject>();
|
||||||
|
|
||||||
|
AddInspectorValues(objects);
|
||||||
|
|
||||||
// I'd hope there's a better way to do this, but I don't want to bind to each and every property above to watch for changes.
|
// I'd hope there's a better way to do this, but I don't want to bind to each and every property above to watch for changes.
|
||||||
// This is a good middle-ground for the time being.
|
// This is a good middle-ground for the time being.
|
||||||
if (EditorBeatmap.SelectedHitObjects.Count > 0)
|
if (objects.Length > 0)
|
||||||
rollingTextUpdate ??= Scheduler.AddDelayed(updateInspectorText, 250);
|
rollingTextUpdate ??= Scheduler.AddDelayed(updateInspectorText, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void AddInspectorValues()
|
protected virtual void AddInspectorValues(HitObject[] objects)
|
||||||
{
|
{
|
||||||
switch (EditorBeatmap.SelectedHitObjects.Count)
|
switch (objects.Length)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
AddValue("No selection");
|
AddValue("No selection");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
var selected = EditorBeatmap.SelectedHitObjects.Single();
|
var selected = objects.Single();
|
||||||
|
|
||||||
AddHeader("Type");
|
AddHeader("Type");
|
||||||
AddValue($"{selected.GetType().ReadableName()}");
|
AddValue($"{selected.GetType().ReadableName()}");
|
||||||
@ -105,13 +116,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
AddHeader("Selected Objects");
|
AddHeader("Selected Objects");
|
||||||
AddValue($"{EditorBeatmap.SelectedHitObjects.Count:#,0.##}");
|
AddValue($"{objects.Length:#,0.##}");
|
||||||
|
|
||||||
AddHeader("Start Time");
|
AddHeader("Start Time");
|
||||||
AddValue($"{EditorBeatmap.SelectedHitObjects.Min(o => o.StartTime):#,0.##}ms");
|
AddValue($"{objects.Min(o => o.StartTime):#,0.##}ms");
|
||||||
|
|
||||||
AddHeader("End Time");
|
AddHeader("End Time");
|
||||||
AddValue($"{EditorBeatmap.SelectedHitObjects.Max(o => o.GetEndTime()):#,0.##}ms");
|
AddValue($"{objects.Max(o => o.GetEndTime()):#,0.##}ms");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -16,6 +14,7 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Resources.Localisation.Web;
|
using osu.Game.Resources.Localisation.Web;
|
||||||
@ -50,14 +49,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
private readonly List<SelectionBlueprint<T>> selectedBlueprints;
|
private readonly List<SelectionBlueprint<T>> selectedBlueprints;
|
||||||
|
|
||||||
protected SelectionBox SelectionBox { get; private set; }
|
protected SelectionBox SelectionBox { get; private set; } = null!;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
protected IEditorChangeHandler ChangeHandler { get; private set; }
|
protected IEditorChangeHandler? ChangeHandler { get; private set; }
|
||||||
|
|
||||||
public SelectionRotationHandler RotationHandler { get; private set; }
|
public SelectionRotationHandler RotationHandler { get; private set; } = null!;
|
||||||
|
|
||||||
public SelectionScaleHandler ScaleHandler { get; private set; }
|
public SelectionScaleHandler ScaleHandler { get; private set; } = null!;
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
protected OsuContextMenuContainer? ContextMenuContainer { get; private set; }
|
||||||
|
|
||||||
protected SelectionHandler()
|
protected SelectionHandler()
|
||||||
{
|
{
|
||||||
@ -230,7 +232,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deselect all selected items.
|
/// Deselect all selected items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void DeselectAll() => SelectedItems.Clear();
|
protected void DeselectAll()
|
||||||
|
{
|
||||||
|
SelectedItems.Clear();
|
||||||
|
ContextMenuContainer?.CloseMenu();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle a blueprint becoming selected.
|
/// Handle a blueprint becoming selected.
|
||||||
@ -243,6 +249,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
SelectedItems.Add(blueprint.Item);
|
SelectedItems.Add(blueprint.Item);
|
||||||
|
|
||||||
selectedBlueprints.Add(blueprint);
|
selectedBlueprints.Add(blueprint);
|
||||||
|
|
||||||
|
ContextMenuContainer?.CloseMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -263,7 +271,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <returns>Whether a selection was performed.</returns>
|
/// <returns>Whether a selection was performed.</returns>
|
||||||
internal virtual bool MouseDownSelectionRequested(SelectionBlueprint<T> blueprint, MouseButtonEvent e)
|
internal virtual bool MouseDownSelectionRequested(SelectionBlueprint<T> blueprint, MouseButtonEvent e)
|
||||||
{
|
{
|
||||||
if (e.ShiftPressed && e.Button == MouseButton.Right)
|
if (e.Button == MouseButton.Middle || (e.ShiftPressed && e.Button == MouseButton.Right))
|
||||||
{
|
{
|
||||||
handleQuickDeletion(blueprint);
|
handleQuickDeletion(blueprint);
|
||||||
return true;
|
return true;
|
||||||
|
@ -69,19 +69,24 @@ namespace osu.Game.Screens.Edit.Compose
|
|||||||
if (ruleset == null || composer == null)
|
if (ruleset == null || composer == null)
|
||||||
return base.CreateTimelineContent();
|
return base.CreateTimelineContent();
|
||||||
|
|
||||||
|
TimelineBreakDisplay breakDisplay = new TimelineBreakDisplay
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Height = 0.75f,
|
||||||
|
};
|
||||||
|
|
||||||
return wrapSkinnableContent(new Container
|
return wrapSkinnableContent(new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
|
// We want to display this below hitobjects to better expose placement objects visually.
|
||||||
|
// It needs to be above the blueprint container to handle drags on breaks though.
|
||||||
|
breakDisplay.CreateProxy(),
|
||||||
new TimelineBlueprintContainer(composer),
|
new TimelineBlueprintContainer(composer),
|
||||||
new TimelineBreakDisplay
|
breakDisplay
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Height = 0.75f,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
@ -159,7 +160,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
private string lastSavedHash;
|
private string lastSavedHash;
|
||||||
|
|
||||||
private Container<EditorScreen> screenContainer;
|
private ScreenContainer screenContainer;
|
||||||
|
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
private readonly EditorLoader loader;
|
private readonly EditorLoader loader;
|
||||||
@ -329,7 +330,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
Name = "Screen container",
|
Name = "Screen container",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Top = 40, Bottom = 50 },
|
Padding = new MarginPadding { Top = 40, Bottom = 50 },
|
||||||
Child = screenContainer = new Container<EditorScreen>
|
Child = screenContainer = new ScreenContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
}
|
}
|
||||||
@ -422,6 +423,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
MutationTracker,
|
MutationTracker,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
changeHandler?.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
|
changeHandler?.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
|
||||||
changeHandler?.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
|
changeHandler?.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
|
||||||
|
|
||||||
@ -1007,7 +1009,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
throw new InvalidOperationException("Editor menu bar switched to an unsupported mode");
|
throw new InvalidOperationException("Editor menu bar switched to an unsupported mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadComponentAsync(currentScreen, newScreen =>
|
screenContainer.LoadComponentAsync(currentScreen, newScreen =>
|
||||||
{
|
{
|
||||||
if (newScreen == currentScreen)
|
if (newScreen == currentScreen)
|
||||||
{
|
{
|
||||||
@ -1385,5 +1387,12 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private partial class ScreenContainer : Container<EditorScreen>
|
||||||
|
{
|
||||||
|
public new Task LoadComponentAsync<TLoadable>([NotNull] TLoadable component, Action<TLoadable> onLoaded = null, CancellationToken cancellation = default, Scheduler scheduler = null)
|
||||||
|
where TLoadable : Drawable
|
||||||
|
=> base.LoadComponentAsync(component, onLoaded, cancellation, scheduler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
});
|
});
|
||||||
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", OsuIcon.Online, new Color4(94, 63, 186, 255), onMultiplayer, Key.M));
|
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", OsuIcon.Online, new Color4(94, 63, 186, 255), onMultiplayer, Key.M));
|
||||||
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Tournament, new Color4(94, 63, 186, 255), onPlaylists, Key.L));
|
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Tournament, new Color4(94, 63, 186, 255), onPlaylists, Key.L));
|
||||||
buttonsPlay.Add(new DailyChallengeButton(@"button-default-select", new Color4(94, 63, 186, 255), onDailyChallenge, Key.D));
|
buttonsPlay.Add(new DailyChallengeButton(@"button-daily-select", new Color4(94, 63, 186, 255), onDailyChallenge, Key.D));
|
||||||
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
||||||
|
|
||||||
buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", OsuIcon.Beatmap, new Color4(238, 170, 0, 255), _ => OnEditBeatmap?.Invoke(), Key.B, Key.E)
|
buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", OsuIcon.Beatmap, new Color4(238, 170, 0, 255), _ => OnEditBeatmap?.Invoke(), Key.B, Key.E)
|
||||||
|
@ -104,8 +104,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
info.BindValueChanged(_ => dailyChallengeChanged(postNotification: true));
|
info.BindValueChanged(dailyChallengeChanged, true);
|
||||||
dailyChallengeChanged(postNotification: false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -131,7 +130,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dailyChallengeChanged(bool postNotification)
|
private long? lastNotifiedDailyChallengeRoomId;
|
||||||
|
|
||||||
|
private void dailyChallengeChanged(ValueChangedEvent<DailyChallengeInfo?> _)
|
||||||
{
|
{
|
||||||
UpdateState();
|
UpdateState();
|
||||||
|
|
||||||
@ -152,8 +153,14 @@ namespace osu.Game.Screens.Menu
|
|||||||
Room = room;
|
Room = room;
|
||||||
cover.OnlineInfo = TooltipContent = room.Playlist.FirstOrDefault()?.Beatmap.BeatmapSet as APIBeatmapSet;
|
cover.OnlineInfo = TooltipContent = room.Playlist.FirstOrDefault()?.Beatmap.BeatmapSet as APIBeatmapSet;
|
||||||
|
|
||||||
if (postNotification)
|
// We only want to notify the user if a new challenge recently went live.
|
||||||
|
if (room.StartDate.Value != null
|
||||||
|
&& Math.Abs((DateTimeOffset.Now - room.StartDate.Value!.Value).TotalSeconds) < 1800
|
||||||
|
&& room.RoomID.Value != lastNotifiedDailyChallengeRoomId)
|
||||||
|
{
|
||||||
|
lastNotifiedDailyChallengeRoomId = room.RoomID.Value;
|
||||||
notificationOverlay?.Post(new NewDailyChallengeNotification(room));
|
notificationOverlay?.Post(new NewDailyChallengeNotification(room));
|
||||||
|
}
|
||||||
|
|
||||||
updateCountdown();
|
updateCountdown();
|
||||||
Scheduler.AddDelayed(updateCountdown, 1000, true);
|
Scheduler.AddDelayed(updateCountdown, 1000, true);
|
||||||
|
@ -90,7 +90,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool UsingThemedIntro { get; private set; }
|
protected bool UsingThemedIntro { get; private set; }
|
||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(false)
|
protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault
|
||||||
{
|
{
|
||||||
Colour = Color4.Black
|
Colour = Color4.Black
|
||||||
};
|
};
|
||||||
|
@ -150,7 +150,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
OnPlaylists = () => this.Push(new Playlists()),
|
OnPlaylists = () => this.Push(new Playlists()),
|
||||||
OnDailyChallenge = room =>
|
OnDailyChallenge = room =>
|
||||||
{
|
{
|
||||||
this.Push(new DailyChallenge(room));
|
this.Push(new DailyChallengeIntro(room));
|
||||||
},
|
},
|
||||||
OnExit = () =>
|
OnExit = () =>
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,6 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
private PlaylistItemBackground? background;
|
private PlaylistItemBackground? background;
|
||||||
|
|
||||||
protected OnlinePlayBackgroundScreen()
|
protected OnlinePlayBackgroundScreen()
|
||||||
: base(false)
|
|
||||||
{
|
{
|
||||||
AddInternal(new Box
|
AddInternal(new Box
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
@ -44,7 +45,7 @@ using osuTK;
|
|||||||
namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||||
{
|
{
|
||||||
[Cached(typeof(IPreviewTrackOwner))]
|
[Cached(typeof(IPreviewTrackOwner))]
|
||||||
public partial class DailyChallenge : OsuScreen, IPreviewTrackOwner
|
public partial class DailyChallenge : OsuScreen, IPreviewTrackOwner, IHandlePresentBeatmap
|
||||||
{
|
{
|
||||||
private readonly Room room;
|
private readonly Room room;
|
||||||
private readonly PlaylistItem playlistItem;
|
private readonly PlaylistItem playlistItem;
|
||||||
@ -76,6 +77,9 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
[Cached]
|
[Cached]
|
||||||
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
|
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuGame? game { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private BeatmapManager beatmapManager { get; set; } = null!;
|
private BeatmapManager beatmapManager { get; set; } = null!;
|
||||||
|
|
||||||
@ -386,7 +390,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
beatmapAvailabilityTracker.SelectedItem.Value = playlistItem;
|
beatmapAvailabilityTracker.SelectedItem.Value = playlistItem;
|
||||||
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => trySetDailyChallengeBeatmap(), true);
|
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => TrySetDailyChallengeBeatmap(this, beatmapManager, rulesets, musicController, playlistItem), true);
|
||||||
|
|
||||||
userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(userModsSelectOverlay);
|
userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(userModsSelectOverlay);
|
||||||
userModsSelectOverlay.SelectedItem.Value = playlistItem;
|
userModsSelectOverlay.SelectedItem.Value = playlistItem;
|
||||||
@ -398,15 +402,6 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
dailyChallengeInfo.BindValueChanged(dailyChallengeChanged);
|
dailyChallengeInfo.BindValueChanged(dailyChallengeChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trySetDailyChallengeBeatmap()
|
|
||||||
{
|
|
||||||
var beatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == playlistItem.Beatmap.OnlineID);
|
|
||||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmap); // this will gracefully fall back to dummy beatmap if missing locally.
|
|
||||||
Ruleset.Value = rulesets.GetRuleset(playlistItem.RulesetID);
|
|
||||||
|
|
||||||
applyLoopingToTrack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() =>
|
private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() =>
|
||||||
{
|
{
|
||||||
if (state.NewValue != APIState.Online)
|
if (state.NewValue != APIState.Online)
|
||||||
@ -440,7 +435,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
|
|
||||||
waves.Show();
|
waves.Show();
|
||||||
roomManager.JoinRoom(room);
|
roomManager.JoinRoom(room);
|
||||||
applyLoopingToTrack();
|
startLoopingTrack(this, musicController);
|
||||||
|
|
||||||
metadataClient.BeginWatchingMultiplayerRoom(room.RoomID.Value!.Value).ContinueWith(t =>
|
metadataClient.BeginWatchingMultiplayerRoom(room.RoomID.Value!.Value).ContinueWith(t =>
|
||||||
{
|
{
|
||||||
@ -452,6 +447,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
|
|
||||||
MultiplayerPlaylistItemStats[] stats = t.GetResultSafely();
|
MultiplayerPlaylistItemStats[] stats = t.GetResultSafely();
|
||||||
var itemStats = stats.SingleOrDefault(item => item.PlaylistItemID == playlistItem.ID);
|
var itemStats = stats.SingleOrDefault(item => item.PlaylistItemID == playlistItem.ID);
|
||||||
|
|
||||||
if (itemStats == null) return;
|
if (itemStats == null) return;
|
||||||
|
|
||||||
Schedule(() =>
|
Schedule(() =>
|
||||||
@ -459,17 +455,17 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
breakdown.SetInitialCounts(itemStats.TotalScoreDistribution);
|
breakdown.SetInitialCounts(itemStats.TotalScoreDistribution);
|
||||||
totals.SetInitialCounts(itemStats.TotalScoreDistribution.Sum(c => c), itemStats.CumulativeScore);
|
totals.SetInitialCounts(itemStats.TotalScoreDistribution.Sum(c => c), itemStats.CumulativeScore);
|
||||||
});
|
});
|
||||||
});
|
}, TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||||
|
|
||||||
beatmapAvailabilityTracker.SelectedItem.Value = playlistItem;
|
|
||||||
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => trySetDailyChallengeBeatmap(), true);
|
|
||||||
userModsSelectOverlay.SelectedItem.Value = playlistItem;
|
userModsSelectOverlay.SelectedItem.Value = playlistItem;
|
||||||
|
|
||||||
|
TrySetDailyChallengeBeatmap(this, beatmapManager, rulesets, musicController, playlistItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnResuming(ScreenTransitionEvent e)
|
public override void OnResuming(ScreenTransitionEvent e)
|
||||||
{
|
{
|
||||||
base.OnResuming(e);
|
base.OnResuming(e);
|
||||||
applyLoopingToTrack();
|
startLoopingTrack(this, musicController);
|
||||||
// re-apply mods as they may have been changed by a child screen
|
// re-apply mods as they may have been changed by a child screen
|
||||||
// (one known instance of this is showing a replay).
|
// (one known instance of this is showing a replay).
|
||||||
updateMods();
|
updateMods();
|
||||||
@ -498,17 +494,30 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
return base.OnExiting(e);
|
return base.OnExiting(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyLoopingToTrack()
|
public static void TrySetDailyChallengeBeatmap(OsuScreen screen, BeatmapManager beatmaps, RulesetStore rulesets, MusicController music, PlaylistItem item)
|
||||||
{
|
{
|
||||||
if (!this.IsCurrentScreen())
|
if (!screen.IsCurrentScreen())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var track = Beatmap.Value?.Track;
|
var beatmap = beatmaps.QueryBeatmap(b => b.OnlineID == item.Beatmap.OnlineID);
|
||||||
|
|
||||||
|
screen.Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); // this will gracefully fall back to dummy beatmap if missing locally.
|
||||||
|
screen.Ruleset.Value = rulesets.GetRuleset(item.RulesetID);
|
||||||
|
|
||||||
|
startLoopingTrack(screen, music);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void startLoopingTrack(OsuScreen screen, MusicController music)
|
||||||
|
{
|
||||||
|
if (!screen.IsCurrentScreen())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var track = screen.Beatmap.Value?.Track;
|
||||||
|
|
||||||
if (track != null)
|
if (track != null)
|
||||||
{
|
{
|
||||||
Beatmap.Value?.PrepareTrackForPreview(true);
|
screen.Beatmap.Value?.PrepareTrackForPreview(true);
|
||||||
musicController.EnsurePlayingSomething();
|
music.EnsurePlayingSomething();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,5 +555,21 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
if (metadataClient.IsNotNull())
|
if (metadataClient.IsNotNull())
|
||||||
metadataClient.MultiplayerRoomScoreSet -= onRoomScoreSet;
|
metadataClient.MultiplayerRoomScoreSet -= onRoomScoreSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset)
|
||||||
|
{
|
||||||
|
if (!this.IsCurrentScreen())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We can only handle the current daily challenge beatmap.
|
||||||
|
// If the import was for a different beatmap, pass the duty off to global handling.
|
||||||
|
if (beatmap.BeatmapSetInfo.OnlineID != playlistItem.Beatmap.BeatmapSet!.OnlineID)
|
||||||
|
{
|
||||||
|
this.Exit();
|
||||||
|
game?.PresentBeatmap(beatmap.BeatmapSetInfo, b => b.ID == beatmap.BeatmapInfo.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And if we're handling, we don't really have much to do here.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,534 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Drawables;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Screens.OnlinePlay.Match;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||||
|
{
|
||||||
|
public partial class DailyChallengeIntro : OsuScreen
|
||||||
|
{
|
||||||
|
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
||||||
|
|
||||||
|
public override bool? ApplyModTrackAdjustments => true;
|
||||||
|
|
||||||
|
private readonly Room room;
|
||||||
|
private readonly PlaylistItem item;
|
||||||
|
|
||||||
|
private Container introContent = null!;
|
||||||
|
private Container topTitleDisplay = null!;
|
||||||
|
private Container bottomDateDisplay = null!;
|
||||||
|
private Container beatmapBackground = null!;
|
||||||
|
private Box flash = null!;
|
||||||
|
|
||||||
|
private FillFlowContainer beatmapContent = null!;
|
||||||
|
|
||||||
|
private Container titleContainer = null!;
|
||||||
|
|
||||||
|
private bool beatmapBackgroundLoaded;
|
||||||
|
|
||||||
|
private bool animationBegan;
|
||||||
|
|
||||||
|
private IBindable<StarDifficulty?> starDifficulty = null!;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
|
||||||
|
|
||||||
|
private bool shouldBePlayingMusic;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private BeatmapManager beatmapManager { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesets { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private MusicController musicController { get; set; } = null!;
|
||||||
|
|
||||||
|
private Sample? dateWindupSample;
|
||||||
|
private Sample? dateImpactSample;
|
||||||
|
private Sample? beatmapWindupSample;
|
||||||
|
private Sample? beatmapImpactSample;
|
||||||
|
|
||||||
|
private SampleChannel? dateWindupChannel;
|
||||||
|
private SampleChannel? dateImpactChannel;
|
||||||
|
private SampleChannel? beatmapWindupChannel;
|
||||||
|
private SampleChannel? beatmapImpactChannel;
|
||||||
|
|
||||||
|
private IDisposable? duckOperation;
|
||||||
|
|
||||||
|
public DailyChallengeIntro(Room room)
|
||||||
|
{
|
||||||
|
this.room = room;
|
||||||
|
item = room.Playlist.Single();
|
||||||
|
|
||||||
|
ValidForResume = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override BackgroundScreen CreateBackground() => new DailyChallengeIntroBackgroundScreen(colourProvider);
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(BeatmapDifficultyCache difficultyCache, BeatmapModelDownloader beatmapDownloader, OsuConfigManager config, AudioManager audio)
|
||||||
|
{
|
||||||
|
const float horizontal_info_size = 500f;
|
||||||
|
|
||||||
|
Ruleset ruleset = Ruleset.Value.CreateInstance();
|
||||||
|
|
||||||
|
StarRatingDisplay starRatingDisplay;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
beatmapAvailabilityTracker,
|
||||||
|
introContent = new Container
|
||||||
|
{
|
||||||
|
Alpha = 0f,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Shear = new Vector2(OsuGame.SHEAR, 0f),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
titleContainer = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
topTitleDisplay = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
CornerRadius = 10f,
|
||||||
|
Masking = true,
|
||||||
|
X = -10,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Background3,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Text = "Today's Challenge",
|
||||||
|
Margin = new MarginPadding { Horizontal = 10f, Vertical = 5f },
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0f),
|
||||||
|
Font = OsuFont.GetFont(size: 32, weight: FontWeight.Light, typeface: Typeface.TorusAlternate),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bottomDateDisplay = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
CornerRadius = 10f,
|
||||||
|
Masking = true,
|
||||||
|
X = 10,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Background3,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Text = room.Name.Value.Split(':', StringSplitOptions.TrimEntries).Last(),
|
||||||
|
Margin = new MarginPadding { Horizontal = 10f, Vertical = 5f },
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0f),
|
||||||
|
Font = OsuFont.GetFont(size: 32, weight: FontWeight.Light, typeface: Typeface.TorusAlternate),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beatmapContent = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AlwaysPresent = true, // so we can get the size ahead of time
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Alpha = 0,
|
||||||
|
Scale = new Vector2(0.001f),
|
||||||
|
Spacing = new Vector2(10),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
beatmapBackground = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Size = new Vector2(horizontal_info_size, 150f),
|
||||||
|
CornerRadius = 20f,
|
||||||
|
BorderColour = colourProvider.Content2,
|
||||||
|
BorderThickness = 3f,
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Background3,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
flash = new Box
|
||||||
|
{
|
||||||
|
Colour = Color4.White,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Depth = float.MinValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Width = horizontal_info_size,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
CornerRadius = 10f,
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Background3,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Padding = new MarginPadding(5f),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new TruncatingSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0f),
|
||||||
|
MaxWidth = horizontal_info_size,
|
||||||
|
Text = item.Beatmap.BeatmapSet!.Metadata.GetDisplayTitleRomanisable(false),
|
||||||
|
Padding = new MarginPadding { Horizontal = 5f },
|
||||||
|
Font = OsuFont.GetFont(size: 26),
|
||||||
|
},
|
||||||
|
new TruncatingSpriteText
|
||||||
|
{
|
||||||
|
Text = $"Difficulty: {item.Beatmap.DifficultyName}",
|
||||||
|
Font = OsuFont.GetFont(size: 20, italics: true),
|
||||||
|
MaxWidth = horizontal_info_size,
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0f),
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
},
|
||||||
|
new TruncatingSpriteText
|
||||||
|
{
|
||||||
|
Text = $"by {item.Beatmap.Metadata.Author.Username}",
|
||||||
|
Font = OsuFont.GetFont(size: 16, italics: true),
|
||||||
|
MaxWidth = horizontal_info_size,
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0f),
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
},
|
||||||
|
starRatingDisplay = new StarRatingDisplay(default)
|
||||||
|
{
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0f),
|
||||||
|
Margin = new MarginPadding(5),
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Width = horizontal_info_size,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
CornerRadius = 10f,
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Background3,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new ModFlowDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0f),
|
||||||
|
Current =
|
||||||
|
{
|
||||||
|
Value = item.RequiredMods.Select(m => m.ToMod(ruleset)).ToArray()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
starDifficulty = difficultyCache.GetBindableDifficulty(item.Beatmap);
|
||||||
|
starDifficulty.BindValueChanged(star =>
|
||||||
|
{
|
||||||
|
if (star.NewValue != null)
|
||||||
|
starRatingDisplay.Current.Value = star.NewValue.Value;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
LoadComponentAsync(new OnlineBeatmapSetCover(item.Beatmap.BeatmapSet as IBeatmapSetOnlineInfo)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
FillMode = FillMode.Fit,
|
||||||
|
Scale = new Vector2(1.2f),
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0f),
|
||||||
|
}, c =>
|
||||||
|
{
|
||||||
|
beatmapBackground.Add(c);
|
||||||
|
|
||||||
|
beatmapBackgroundLoaded = true;
|
||||||
|
updateAnimationState();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (config.Get<bool>(OsuSetting.AutomaticallyDownloadMissingBeatmaps))
|
||||||
|
{
|
||||||
|
if (!beatmapManager.IsAvailableLocally(new BeatmapSetInfo { OnlineID = item.Beatmap.BeatmapSet!.OnlineID }))
|
||||||
|
beatmapDownloader.Download(item.Beatmap.BeatmapSet!, config.Get<bool>(OsuSetting.PreferNoVideo));
|
||||||
|
}
|
||||||
|
|
||||||
|
dateWindupSample = audio.Samples.Get(@"DailyChallenge/date-windup");
|
||||||
|
dateImpactSample = audio.Samples.Get(@"DailyChallenge/date-impact");
|
||||||
|
beatmapWindupSample = audio.Samples.Get(@"DailyChallenge/beatmap-windup");
|
||||||
|
beatmapImpactSample = audio.Samples.Get(@"DailyChallenge/beatmap-impact");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnEntering(ScreenTransitionEvent e)
|
||||||
|
{
|
||||||
|
base.OnEntering(e);
|
||||||
|
|
||||||
|
beatmapAvailabilityTracker.SelectedItem.Value = item;
|
||||||
|
beatmapAvailabilityTracker.Availability.BindValueChanged(availability =>
|
||||||
|
{
|
||||||
|
if (shouldBePlayingMusic && availability.NewValue.State == DownloadState.LocallyAvailable)
|
||||||
|
DailyChallenge.TrySetDailyChallengeBeatmap(this, beatmapManager, rulesets, musicController, item);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
this.FadeInFromZero(400, Easing.OutQuint);
|
||||||
|
updateAnimationState();
|
||||||
|
|
||||||
|
playDateWindupSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSuspending(ScreenTransitionEvent e)
|
||||||
|
{
|
||||||
|
this.FadeOut(800, Easing.OutQuint);
|
||||||
|
base.OnSuspending(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAnimationState()
|
||||||
|
{
|
||||||
|
if (!beatmapBackgroundLoaded || !this.IsCurrentScreen())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (animationBegan)
|
||||||
|
return;
|
||||||
|
|
||||||
|
beginAnimation();
|
||||||
|
animationBegan = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beginAnimation()
|
||||||
|
{
|
||||||
|
using (BeginDelayedSequence(200))
|
||||||
|
{
|
||||||
|
introContent.Show();
|
||||||
|
|
||||||
|
const float y_offset_start = 260;
|
||||||
|
const float y_offset_end = 20;
|
||||||
|
|
||||||
|
topTitleDisplay
|
||||||
|
.FadeInFromZero(400, Easing.OutQuint);
|
||||||
|
|
||||||
|
topTitleDisplay.MoveToY(-y_offset_start)
|
||||||
|
.MoveToY(-y_offset_end, 300, Easing.OutQuint)
|
||||||
|
.Then()
|
||||||
|
.MoveToY(0, 4000);
|
||||||
|
|
||||||
|
bottomDateDisplay.MoveToY(y_offset_start)
|
||||||
|
.MoveToY(y_offset_end, 300, Easing.OutQuint)
|
||||||
|
.Then()
|
||||||
|
.MoveToY(0, 4000);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(150))
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
playDateImpactSample();
|
||||||
|
playBeatmapWindupSample();
|
||||||
|
|
||||||
|
duckOperation?.Dispose();
|
||||||
|
duckOperation = musicController.Duck(new DuckParameters
|
||||||
|
{
|
||||||
|
RestoreDuration = 1500f,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(2750))
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
duckOperation?.Dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(1000))
|
||||||
|
{
|
||||||
|
beatmapContent
|
||||||
|
.ScaleTo(3)
|
||||||
|
.ScaleTo(1f, 500, Easing.In)
|
||||||
|
.Then()
|
||||||
|
.ScaleTo(1.1f, 4000);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(100))
|
||||||
|
{
|
||||||
|
titleContainer
|
||||||
|
.ScaleTo(0.4f, 400, Easing.In)
|
||||||
|
.FadeOut(500, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(240))
|
||||||
|
{
|
||||||
|
beatmapContent.FadeInFromZero(280, Easing.InQuad);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(300))
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
shouldBePlayingMusic = true;
|
||||||
|
DailyChallenge.TrySetDailyChallengeBeatmap(this, beatmapManager, rulesets, musicController, item);
|
||||||
|
ApplyToBackground(bs => ((RoomBackgroundScreen)bs).SelectedItem.Value = item);
|
||||||
|
playBeatmapImpactSample();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(400))
|
||||||
|
flash.FadeOutFromOne(5000, Easing.OutQuint);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(2600))
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
if (this.IsCurrentScreen())
|
||||||
|
this.Push(new DailyChallenge(room));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playDateWindupSample()
|
||||||
|
{
|
||||||
|
dateWindupChannel = dateWindupSample?.GetChannel();
|
||||||
|
dateWindupChannel?.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playDateImpactSample()
|
||||||
|
{
|
||||||
|
dateImpactChannel = dateImpactSample?.GetChannel();
|
||||||
|
dateImpactChannel?.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playBeatmapWindupSample()
|
||||||
|
{
|
||||||
|
beatmapWindupChannel = beatmapWindupSample?.GetChannel();
|
||||||
|
beatmapWindupChannel?.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playBeatmapImpactSample()
|
||||||
|
{
|
||||||
|
beatmapImpactChannel = beatmapImpactSample?.GetChannel();
|
||||||
|
beatmapImpactChannel?.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
resetAudio();
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetAudio()
|
||||||
|
{
|
||||||
|
dateWindupChannel?.Stop();
|
||||||
|
dateImpactChannel?.Stop();
|
||||||
|
beatmapWindupChannel?.Stop();
|
||||||
|
beatmapImpactChannel?.Stop();
|
||||||
|
duckOperation?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class DailyChallengeIntroBackgroundScreen : RoomBackgroundScreen
|
||||||
|
{
|
||||||
|
private readonly OverlayColourProvider colourProvider;
|
||||||
|
|
||||||
|
public DailyChallengeIntroBackgroundScreen(OverlayColourProvider colourProvider)
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
this.colourProvider = colourProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AddInternal(new Box
|
||||||
|
{
|
||||||
|
Depth = float.MinValue,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background5.Opacity(0.6f),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -41,7 +42,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
|
|
||||||
Background.Alpha = 0.2f;
|
Background.Alpha = 0.2f;
|
||||||
|
|
||||||
TextBox.FocusLost = () => expandedFromTextBoxFocus.Value = false;
|
TextBox.PlaceholderText = ChatStrings.InGameInputPlaceholder;
|
||||||
|
TextBox.Focus = () => TextBox.PlaceholderText = Resources.Localisation.Web.ChatStrings.InputPlaceholder;
|
||||||
|
TextBox.FocusLost = () =>
|
||||||
|
{
|
||||||
|
TextBox.PlaceholderText = ChatStrings.InGameInputPlaceholder;
|
||||||
|
expandedFromTextBoxFocus.Value = false;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e) => true; // use UI mouse cursor.
|
protected override bool OnHover(HoverEvent e) => true; // use UI mouse cursor.
|
||||||
|
@ -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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
|
@ -14,11 +14,10 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public ArgonKeyCounterDisplay()
|
public ArgonKeyCounterDisplay()
|
||||||
{
|
{
|
||||||
InternalChild = KeyFlow = new FillFlowContainer<KeyCounter>
|
Child = KeyFlow = new FillFlowContainer<KeyCounter>
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Alpha = 0,
|
|
||||||
Spacing = new Vector2(2),
|
Spacing = new Vector2(2),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user