mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 18:23:04 +08:00
Merge branch 'master' into chat-mention
This commit is contained in:
commit
26312bf60a
@ -51,7 +51,7 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.601.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.604.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.609.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -20,7 +20,8 @@ namespace osu.Android
|
||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
||||
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed" })]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-archive")]
|
||||
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed", "application/x-osu-archive" })]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })]
|
||||
public class OsuGameActivity : AndroidGameActivity
|
||||
{
|
||||
|
@ -0,0 +1,2 @@
|
||||
[General]
|
||||
// no version specified means v1
|
@ -4,10 +4,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
@ -21,12 +19,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public class TestSceneCatchModHidden : ModTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
LocalConfig.SetValue(OsuSetting.IncreaseFirstObjectVisibility, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestJuiceStream()
|
||||
{
|
||||
|
@ -8,9 +8,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
Fruit,
|
||||
Banana,
|
||||
Droplet,
|
||||
CatcherIdle,
|
||||
CatcherFail,
|
||||
CatcherKiai,
|
||||
Catcher,
|
||||
CatchComboCounter
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
public class CatchDifficultyAttributes : DifficultyAttributes
|
||||
{
|
||||
public double ApproachRate;
|
||||
public double ApproachRate { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -39,10 +39,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
tinyTicksMissed = Score.Statistics.GetOrDefault(HitResult.SmallTickMiss);
|
||||
misses = Score.Statistics.GetOrDefault(HitResult.Miss);
|
||||
|
||||
// Don't count scores made with supposedly unranked mods
|
||||
if (mods.Any(m => !m.Ranked))
|
||||
return 0;
|
||||
|
||||
// We are heavily relying on aim in catch the beat
|
||||
double value = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0049) - 4.0, 2.0) / 100000.0;
|
||||
|
||||
|
@ -10,7 +10,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
public class CatchModHardRock : ModHardRock, IApplicableToBeatmap
|
||||
{
|
||||
public override double ScoreMultiplier => 1.12;
|
||||
public override bool Ranked => true;
|
||||
|
||||
public void ApplyToBeatmap(IBeatmap beatmap) => CatchBeatmapProcessor.ApplyPositionOffsets(beatmap, this);
|
||||
}
|
||||
|
@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
}
|
||||
|
||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
}
|
||||
=> ApplyNormalVisibilityState(hitObject, state);
|
||||
|
||||
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
|
@ -33,13 +33,13 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
|
||||
private class MouseInputHelper : Drawable, IKeyBindingHandler<CatchAction>, IRequireHighFrequencyMousePosition
|
||||
{
|
||||
private readonly Catcher catcher;
|
||||
private readonly CatcherArea catcherArea;
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
public MouseInputHelper(CatchPlayfield playfield)
|
||||
{
|
||||
catcher = playfield.CatcherArea.MovableCatcher;
|
||||
catcherArea = playfield.CatcherArea;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
catcher.UpdatePosition(e.MousePosition.X / DrawSize.X * CatchPlayfield.WIDTH);
|
||||
catcherArea.SetCatcherPosition(e.MousePosition.X / DrawSize.X * CatchPlayfield.WIDTH);
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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.Bindables;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
@ -9,21 +8,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
/// <summary>
|
||||
/// Represents a <see cref="Fruit"/> caught by the catcher.
|
||||
/// </summary>
|
||||
public class CaughtFruit : CaughtObject, IHasFruitState
|
||||
public class CaughtFruit : CaughtObject
|
||||
{
|
||||
public Bindable<FruitVisualRepresentation> VisualRepresentation { get; } = new Bindable<FruitVisualRepresentation>();
|
||||
|
||||
public CaughtFruit()
|
||||
: base(CatchSkinComponents.Fruit, _ => new FruitPiece())
|
||||
{
|
||||
}
|
||||
|
||||
public override void CopyStateFrom(IHasCatchObjectState objectState)
|
||||
{
|
||||
base.CopyStateFrom(objectState);
|
||||
|
||||
var fruitState = (IHasFruitState)objectState;
|
||||
VisualRepresentation.Value = fruitState.VisualRepresentation.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
public PalpableCatchHitObject HitObject { get; private set; }
|
||||
public Bindable<Color4> AccentColour { get; } = new Bindable<Color4>();
|
||||
public Bindable<bool> HyperDash { get; } = new Bindable<bool>();
|
||||
public Bindable<int> IndexInBeatmap { get; } = new Bindable<int>();
|
||||
|
||||
public Vector2 DisplaySize => Size * Scale;
|
||||
|
||||
@ -51,6 +52,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
Rotation = objectState.DisplayRotation;
|
||||
AccentColour.Value = objectState.AccentColour.Value;
|
||||
HyperDash.Value = objectState.HyperDash.Value;
|
||||
IndexInBeatmap.Value = objectState.IndexInBeatmap.Value;
|
||||
}
|
||||
|
||||
protected override void FreeAfterUse()
|
||||
|
@ -3,17 +3,14 @@
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
{
|
||||
public class DrawableFruit : DrawablePalpableCatchHitObject, IHasFruitState
|
||||
public class DrawableFruit : DrawablePalpableCatchHitObject
|
||||
{
|
||||
public Bindable<FruitVisualRepresentation> VisualRepresentation { get; } = new Bindable<FruitVisualRepresentation>();
|
||||
|
||||
public DrawableFruit()
|
||||
: this(null)
|
||||
{
|
||||
@ -27,11 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
IndexInBeatmap.BindValueChanged(change =>
|
||||
{
|
||||
VisualRepresentation.Value = (FruitVisualRepresentation)(change.NewValue % 4);
|
||||
}, true);
|
||||
|
||||
ScalingContainer.Child = new SkinnableDrawable(
|
||||
new CatchSkinComponent(CatchSkinComponents.Fruit),
|
||||
_ => new FruitPiece());
|
||||
@ -44,12 +36,4 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
ScalingContainer.RotateTo((RandomSingle(1) - 0.5f) * 40);
|
||||
}
|
||||
}
|
||||
|
||||
public enum FruitVisualRepresentation
|
||||
{
|
||||
Pear,
|
||||
Grape,
|
||||
Pineapple,
|
||||
Raspberry,
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
|
||||
Bindable<bool> HyperDash { get; }
|
||||
|
||||
Bindable<int> IndexInBeatmap { get; }
|
||||
|
||||
Vector2 DisplaySize { get; }
|
||||
|
||||
float DisplayRotation { get; }
|
||||
|
@ -1,15 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a visual state of a <see cref="Fruit"/>.
|
||||
/// </summary>
|
||||
public interface IHasFruitState : IHasCatchObjectState
|
||||
{
|
||||
Bindable<FruitVisualRepresentation> VisualRepresentation { get; }
|
||||
}
|
||||
}
|
@ -9,5 +9,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
public class Fruit : PalpableCatchHitObject
|
||||
{
|
||||
public override Judgement CreateJudgement() => new CatchJudgement();
|
||||
|
||||
public static FruitVisualRepresentation GetVisualRepresentation(int indexInBeatmap) => (FruitVisualRepresentation)(indexInBeatmap % 4);
|
||||
}
|
||||
}
|
||||
|
13
osu.Game.Rulesets.Catch/Objects/FruitVisualRepresentation.cs
Normal file
13
osu.Game.Rulesets.Catch/Objects/FruitVisualRepresentation.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public enum FruitVisualRepresentation
|
||||
{
|
||||
Pear,
|
||||
Grape,
|
||||
Pineapple,
|
||||
Raspberry,
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Scoring
|
||||
{
|
||||
public class CatchHitWindows : HitWindows
|
||||
{
|
||||
public override bool IsHitResultAllowed(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case HitResult.Great:
|
||||
case HitResult.Miss:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
|
||||
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
|
||||
public readonly Bindable<int> IndexInBeatmap = new Bindable<int>();
|
||||
|
||||
[Resolved]
|
||||
protected IHasCatchObjectState ObjectState { get; private set; }
|
||||
@ -37,6 +38,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
|
||||
AccentColour.BindTo(ObjectState.AccentColour);
|
||||
HyperDash.BindTo(ObjectState.HyperDash);
|
||||
IndexInBeatmap.BindTo(ObjectState.IndexInBeatmap);
|
||||
|
||||
HyperDash.BindValueChanged(hyper =>
|
||||
{
|
||||
|
54
osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs
Normal file
54
osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.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 System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
public class DefaultCatcher : CompositeDrawable, ICatcherSprite
|
||||
{
|
||||
public Bindable<CatcherAnimationState> CurrentState { get; } = new Bindable<CatcherAnimationState>();
|
||||
|
||||
public Texture CurrentTexture => sprite.Texture;
|
||||
|
||||
private readonly Sprite sprite;
|
||||
|
||||
private readonly Dictionary<CatcherAnimationState, Texture> textures = new Dictionary<CatcherAnimationState, Texture>();
|
||||
|
||||
public DefaultCatcher()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
InternalChild = sprite = new Sprite
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore store, Bindable<CatcherAnimationState> currentState)
|
||||
{
|
||||
CurrentState.BindTo(currentState);
|
||||
|
||||
textures[CatcherAnimationState.Idle] = store.Get(@"Gameplay/catch/fruit-catcher-idle");
|
||||
textures[CatcherAnimationState.Fail] = store.Get(@"Gameplay/catch/fruit-catcher-fail");
|
||||
textures[CatcherAnimationState.Kiai] = store.Get(@"Gameplay/catch/fruit-catcher-kiai");
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
CurrentState.BindValueChanged(state => sprite.Texture = textures[state.NewValue], true);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
@ -39,8 +39,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
var fruitState = (IHasFruitState)ObjectState;
|
||||
VisualRepresentation.BindTo(fruitState.VisualRepresentation);
|
||||
IndexInBeatmap.BindValueChanged(index =>
|
||||
{
|
||||
VisualRepresentation.Value = Fruit.GetVisualRepresentation(index.NewValue);
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
|
12
osu.Game.Rulesets.Catch/Skinning/ICatcherSprite.cs
Normal file
12
osu.Game.Rulesets.Catch/Skinning/ICatcherSprite.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning
|
||||
{
|
||||
public interface ICatcherSprite
|
||||
{
|
||||
Texture CurrentTexture { get; }
|
||||
}
|
||||
}
|
@ -65,17 +65,21 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.CatcherIdle:
|
||||
return this.GetAnimation("fruit-catcher-idle", true, true, true) ??
|
||||
this.GetAnimation("fruit-ryuuta", true, true, true);
|
||||
case CatchSkinComponents.Catcher:
|
||||
var version = Source.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value ?? 1;
|
||||
|
||||
case CatchSkinComponents.CatcherFail:
|
||||
return this.GetAnimation("fruit-catcher-fail", true, true, true) ??
|
||||
this.GetAnimation("fruit-ryuuta", true, true, true);
|
||||
if (version < 2.3m)
|
||||
{
|
||||
if (GetTexture(@"fruit-ryuuta") != null ||
|
||||
GetTexture(@"fruit-ryuuta-0") != null)
|
||||
return new LegacyCatcherOld();
|
||||
}
|
||||
|
||||
case CatchSkinComponents.CatcherKiai:
|
||||
return this.GetAnimation("fruit-catcher-kiai", true, true, true) ??
|
||||
this.GetAnimation("fruit-ryuuta", true, true, true);
|
||||
if (GetTexture(@"fruit-catcher-idle") != null ||
|
||||
GetTexture(@"fruit-catcher-idle-0") != null)
|
||||
return new LegacyCatcherNew();
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.CatchComboCounter:
|
||||
if (providesComboCounter)
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
public class LegacyBananaPiece : LegacyCatchHitObjectPiece
|
||||
{
|
@ -13,12 +13,13 @@ using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
public abstract class LegacyCatchHitObjectPiece : PoolableDrawable
|
||||
{
|
||||
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
|
||||
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
|
||||
public readonly Bindable<int> IndexInBeatmap = new Bindable<int>();
|
||||
|
||||
private readonly Sprite colouredSprite;
|
||||
private readonly Sprite overlaySprite;
|
||||
@ -64,6 +65,7 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
||||
|
||||
AccentColour.BindTo(ObjectState.AccentColour);
|
||||
HyperDash.BindTo(ObjectState.HyperDash);
|
||||
IndexInBeatmap.BindTo(ObjectState.IndexInBeatmap);
|
||||
|
||||
hyperSprite.Colour = Skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashFruit)?.Value ??
|
||||
Skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
73
osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs
Normal file
73
osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
public class LegacyCatcherNew : CompositeDrawable, ICatcherSprite
|
||||
{
|
||||
[Resolved]
|
||||
private Bindable<CatcherAnimationState> currentState { get; set; }
|
||||
|
||||
public Texture CurrentTexture => (currentDrawable as TextureAnimation)?.CurrentFrame ?? (currentDrawable as Sprite)?.Texture;
|
||||
|
||||
private readonly Dictionary<CatcherAnimationState, Drawable> drawables = new Dictionary<CatcherAnimationState, Drawable>();
|
||||
|
||||
private Drawable currentDrawable;
|
||||
|
||||
public LegacyCatcherNew()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
foreach (var state in Enum.GetValues(typeof(CatcherAnimationState)).Cast<CatcherAnimationState>())
|
||||
{
|
||||
AddInternal(drawables[state] = getDrawableFor(state).With(d =>
|
||||
{
|
||||
d.Anchor = Anchor.TopCentre;
|
||||
d.Origin = Anchor.TopCentre;
|
||||
d.RelativeSizeAxes = Axes.Both;
|
||||
d.Size = Vector2.One;
|
||||
d.FillMode = FillMode.Fit;
|
||||
d.Alpha = 0;
|
||||
}));
|
||||
}
|
||||
|
||||
currentDrawable = drawables[CatcherAnimationState.Idle];
|
||||
|
||||
Drawable getDrawableFor(CatcherAnimationState state) =>
|
||||
skin.GetAnimation(@$"fruit-catcher-{state.ToString().ToLowerInvariant()}", true, true, true) ??
|
||||
skin.GetAnimation(@"fruit-catcher-idle", true, true, true);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
currentState.BindValueChanged(state =>
|
||||
{
|
||||
currentDrawable.Alpha = 0;
|
||||
currentDrawable = drawables[state.NewValue];
|
||||
currentDrawable.Alpha = 1;
|
||||
|
||||
(currentDrawable as IFramedAnimation)?.GotoFrame(0);
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
37
osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs
Normal file
37
osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs
Normal file
@ -0,0 +1,37 @@
|
||||
// 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.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
public class LegacyCatcherOld : CompositeDrawable, ICatcherSprite
|
||||
{
|
||||
public Texture CurrentTexture => (InternalChild as TextureAnimation)?.CurrentFrame ?? (InternalChild as Sprite)?.Texture;
|
||||
|
||||
public LegacyCatcherOld()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
InternalChild = skin.GetAnimation(@"fruit-ryuuta", true, true, true).With(d =>
|
||||
{
|
||||
d.Anchor = Anchor.TopCentre;
|
||||
d.Origin = Anchor.TopCentre;
|
||||
d.RelativeSizeAxes = Axes.Both;
|
||||
d.Size = Vector2.One;
|
||||
d.FillMode = FillMode.Fit;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
public class LegacyDropletPiece : LegacyCatchHitObjectPiece
|
||||
{
|
@ -1,23 +1,20 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
internal class LegacyFruitPiece : LegacyCatchHitObjectPiece
|
||||
{
|
||||
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>();
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
var fruitState = (IHasFruitState)ObjectState;
|
||||
VisualRepresentation.BindTo(fruitState.VisualRepresentation);
|
||||
|
||||
VisualRepresentation.BindValueChanged(visual => setTexture(visual.NewValue), true);
|
||||
IndexInBeatmap.BindValueChanged(index =>
|
||||
{
|
||||
setTexture(Fruit.GetVisualRepresentation(index.NewValue));
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void setTexture(FruitVisualRepresentation visualRepresentation)
|
||||
|
@ -25,9 +25,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
base.SkinChanged(skin);
|
||||
ComboCounter?.UpdateCombo(currentCombo);
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,9 @@ using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
@ -18,6 +17,7 @@ using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Skinning;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
@ -25,7 +25,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class Catcher : SkinReloadableDrawable, IKeyBindingHandler<CatchAction>
|
||||
public class Catcher : SkinReloadableDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail
|
||||
@ -53,6 +53,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// </summary>
|
||||
public const double BASE_SPEED = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// The current speed of the catcher.
|
||||
/// </summary>
|
||||
public double Speed => (Dashing ? 1 : 0.5) * BASE_SPEED * hyperDashModifier;
|
||||
|
||||
/// <summary>
|
||||
/// The amount by which caught fruit should be offset from the plate surface to make them look visually "caught".
|
||||
/// </summary>
|
||||
@ -78,24 +83,24 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// </summary>
|
||||
private readonly Container<CaughtObject> droppedObjectTarget;
|
||||
|
||||
public CatcherAnimationState CurrentState { get; private set; }
|
||||
[Cached]
|
||||
protected readonly Bindable<CatcherAnimationState> CurrentStateBindable = new Bindable<CatcherAnimationState>();
|
||||
|
||||
public CatcherAnimationState CurrentState => CurrentStateBindable.Value;
|
||||
|
||||
/// <summary>
|
||||
/// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable.
|
||||
/// </summary>
|
||||
public const float ALLOWED_CATCH_RANGE = 0.8f;
|
||||
|
||||
/// <summary>
|
||||
/// The drawable catcher for <see cref="CurrentState"/>.
|
||||
/// </summary>
|
||||
internal Drawable CurrentDrawableCatcher => currentCatcher.Drawable;
|
||||
internal Texture CurrentTexture => ((ICatcherSprite)currentCatcher.Drawable).CurrentTexture;
|
||||
|
||||
private bool dashing;
|
||||
|
||||
public bool Dashing
|
||||
{
|
||||
get => dashing;
|
||||
protected set
|
||||
set
|
||||
{
|
||||
if (value == dashing) return;
|
||||
|
||||
@ -105,22 +110,22 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
}
|
||||
}
|
||||
|
||||
public Direction VisualDirection
|
||||
{
|
||||
get => Scale.X > 0 ? Direction.Right : Direction.Left;
|
||||
set => Scale = new Vector2((value == Direction.Right ? 1 : -1) * Math.Abs(Scale.X), Scale.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Width of the area that can be used to attempt catches during gameplay.
|
||||
/// </summary>
|
||||
private readonly float catchWidth;
|
||||
|
||||
private readonly CatcherSprite catcherIdle;
|
||||
private readonly CatcherSprite catcherKiai;
|
||||
private readonly CatcherSprite catcherFail;
|
||||
|
||||
private CatcherSprite currentCatcher;
|
||||
private readonly SkinnableDrawable currentCatcher;
|
||||
|
||||
private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR;
|
||||
private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR;
|
||||
|
||||
private int currentDirection;
|
||||
|
||||
private double hyperDashModifier = 1;
|
||||
private int hyperDashDirection;
|
||||
private float hyperDashTargetPosition;
|
||||
@ -156,20 +161,12 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
},
|
||||
catcherIdle = new CatcherSprite(CatcherAnimationState.Idle)
|
||||
currentCatcher = new SkinnableDrawable(
|
||||
new CatchSkinComponent(CatchSkinComponents.Catcher),
|
||||
_ => new DefaultCatcher())
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Alpha = 0,
|
||||
},
|
||||
catcherKiai = new CatcherSprite(CatcherAnimationState.Kiai)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Alpha = 0,
|
||||
},
|
||||
catcherFail = new CatcherSprite(CatcherAnimationState.Fail)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Alpha = 0,
|
||||
OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE
|
||||
},
|
||||
hitExplosionContainer = new HitExplosionContainer
|
||||
{
|
||||
@ -184,8 +181,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
hitLighting = config.GetBindable<bool>(OsuSetting.HitLighting);
|
||||
trails = new CatcherTrailDisplay(this);
|
||||
|
||||
updateCatcher();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -328,55 +323,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePosition(float position)
|
||||
{
|
||||
position = Math.Clamp(position, 0, CatchPlayfield.WIDTH);
|
||||
|
||||
if (position == X)
|
||||
return;
|
||||
|
||||
Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y);
|
||||
X = position;
|
||||
}
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection--;
|
||||
return true;
|
||||
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection++;
|
||||
return true;
|
||||
|
||||
case CatchAction.Dash:
|
||||
Dashing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection++;
|
||||
break;
|
||||
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection--;
|
||||
break;
|
||||
|
||||
case CatchAction.Dash:
|
||||
Dashing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drop any fruit off the plate.
|
||||
/// </summary>
|
||||
@ -396,9 +342,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing;
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
base.SkinChanged(skin);
|
||||
|
||||
hyperDashColour =
|
||||
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
||||
@ -418,15 +364,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (currentDirection == 0) return;
|
||||
|
||||
var direction = Math.Sign(currentDirection);
|
||||
|
||||
var dashModifier = Dashing ? 1 : 0.5;
|
||||
var speed = BASE_SPEED * dashModifier * hyperDashModifier;
|
||||
|
||||
UpdatePosition((float)(X + direction * Clock.ElapsedFrameTime * speed));
|
||||
|
||||
// Correct overshooting.
|
||||
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||
(hyperDashDirection < 0 && hyperDashTargetPosition > X))
|
||||
@ -436,36 +373,12 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCatcher()
|
||||
{
|
||||
currentCatcher?.Hide();
|
||||
|
||||
switch (CurrentState)
|
||||
{
|
||||
default:
|
||||
currentCatcher = catcherIdle;
|
||||
break;
|
||||
|
||||
case CatcherAnimationState.Fail:
|
||||
currentCatcher = catcherFail;
|
||||
break;
|
||||
|
||||
case CatcherAnimationState.Kiai:
|
||||
currentCatcher = catcherKiai;
|
||||
break;
|
||||
}
|
||||
|
||||
currentCatcher.Show();
|
||||
(currentCatcher.Drawable as IFramedAnimation)?.GotoFrame(0);
|
||||
}
|
||||
|
||||
private void updateState(CatcherAnimationState state)
|
||||
{
|
||||
if (CurrentState == state)
|
||||
return;
|
||||
|
||||
CurrentState = state;
|
||||
updateCatcher();
|
||||
CurrentStateBindable.Value = state;
|
||||
}
|
||||
|
||||
private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position)
|
||||
|
@ -1,8 +1,10 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
@ -14,13 +16,20 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatcherArea : Container
|
||||
public class CatcherArea : Container, IKeyBindingHandler<CatchAction>
|
||||
{
|
||||
public const float CATCHER_SIZE = 106.75f;
|
||||
|
||||
public readonly Catcher MovableCatcher;
|
||||
private readonly CatchComboDisplay comboDisplay;
|
||||
|
||||
/// <summary>
|
||||
/// <c>-1</c> when only left button is pressed.
|
||||
/// <c>1</c> when only right button is pressed.
|
||||
/// <c>0</c> when none or both left and right buttons are pressed.
|
||||
/// </summary>
|
||||
private int currentDirection;
|
||||
|
||||
public CatcherArea(Container<CaughtObject> droppedObjectContainer, BeatmapDifficulty difficulty = null)
|
||||
{
|
||||
Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE);
|
||||
@ -63,16 +72,73 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
MovableCatcher.OnRevertResult(hitObject, result);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
var replayState = (GetContainingInputManager().CurrentState as RulesetInputManagerInputState<CatchAction>)?.LastReplayState as CatchFramedReplayInputHandler.CatchReplayState;
|
||||
|
||||
SetCatcherPosition(
|
||||
replayState?.CatcherX ??
|
||||
(float)(MovableCatcher.X + MovableCatcher.Speed * currentDirection * Clock.ElapsedFrameTime));
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
var state = (GetContainingInputManager().CurrentState as RulesetInputManagerInputState<CatchAction>)?.LastReplayState as CatchFramedReplayInputHandler.CatchReplayState;
|
||||
|
||||
if (state?.CatcherX != null)
|
||||
MovableCatcher.X = state.CatcherX.Value;
|
||||
|
||||
comboDisplay.X = MovableCatcher.X;
|
||||
}
|
||||
|
||||
public void SetCatcherPosition(float X)
|
||||
{
|
||||
float lastPosition = MovableCatcher.X;
|
||||
float newPosition = Math.Clamp(X, 0, CatchPlayfield.WIDTH);
|
||||
|
||||
MovableCatcher.X = newPosition;
|
||||
|
||||
if (lastPosition < newPosition)
|
||||
MovableCatcher.VisualDirection = Direction.Right;
|
||||
else if (lastPosition > newPosition)
|
||||
MovableCatcher.VisualDirection = Direction.Left;
|
||||
}
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection--;
|
||||
return true;
|
||||
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection++;
|
||||
return true;
|
||||
|
||||
case CatchAction.Dash:
|
||||
MovableCatcher.Dashing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection++;
|
||||
break;
|
||||
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection--;
|
||||
break;
|
||||
|
||||
case CatchAction.Dash:
|
||||
MovableCatcher.Dashing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,59 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatcherSprite : SkinnableDrawable
|
||||
{
|
||||
protected override bool ApplySizeRestrictionsToDefault => true;
|
||||
|
||||
public CatcherSprite(CatcherAnimationState state)
|
||||
: base(new CatchSkinComponent(componentFromState(state)), _ =>
|
||||
new DefaultCatcherSprite(state), confineMode: ConfineMode.ScaleToFit)
|
||||
{
|
||||
RelativeSizeAxes = Axes.None;
|
||||
Size = new Vector2(CatcherArea.CATCHER_SIZE);
|
||||
|
||||
// Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
|
||||
OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE;
|
||||
}
|
||||
|
||||
private static CatchSkinComponents componentFromState(CatcherAnimationState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case CatcherAnimationState.Fail:
|
||||
return CatchSkinComponents.CatcherFail;
|
||||
|
||||
case CatcherAnimationState.Kiai:
|
||||
return CatchSkinComponents.CatcherKiai;
|
||||
|
||||
default:
|
||||
return CatchSkinComponents.CatcherIdle;
|
||||
}
|
||||
}
|
||||
|
||||
private class DefaultCatcherSprite : Sprite
|
||||
{
|
||||
private readonly CatcherAnimationState state;
|
||||
|
||||
public DefaultCatcherSprite(CatcherAnimationState state)
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
Texture = textures.Get($"Gameplay/catch/fruit-catcher-{state.ToString().ToLower()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,10 +4,8 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -120,11 +118,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
private CatcherTrailSprite createTrailSprite(Container<CatcherTrailSprite> target)
|
||||
{
|
||||
var texture = (catcher.CurrentDrawableCatcher as TextureAnimation)?.CurrentFrame ?? ((Sprite)catcher.CurrentDrawableCatcher).Texture;
|
||||
|
||||
CatcherTrailSprite sprite = trailPool.Get();
|
||||
|
||||
sprite.Texture = texture;
|
||||
sprite.Texture = catcher.CurrentTexture;
|
||||
sprite.Anchor = catcher.Anchor;
|
||||
sprite.Scale = catcher.Scale;
|
||||
sprite.Blending = BlendingParameters.Additive;
|
||||
|
11
osu.Game.Rulesets.Catch/UI/Direction.cs
Normal file
11
osu.Game.Rulesets.Catch/UI/Direction.cs
Normal file
@ -0,0 +1,11 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public enum Direction
|
||||
{
|
||||
Right = 1,
|
||||
Left = -1
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
{
|
||||
public class ManiaDifficultyAttributes : DifficultyAttributes
|
||||
{
|
||||
public double GreatHitWindow;
|
||||
public double ScoreMultiplier;
|
||||
public double GreatHitWindow { get; set; }
|
||||
public double ScoreMultiplier { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +44,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
countMeh = Score.Statistics.GetOrDefault(HitResult.Meh);
|
||||
countMiss = Score.Statistics.GetOrDefault(HitResult.Miss);
|
||||
|
||||
if (mods.Any(m => !m.Ranked))
|
||||
return 0;
|
||||
|
||||
IEnumerable<Mod> scoreIncreaseMods = Ruleset.GetModsFor(ModType.DifficultyIncrease);
|
||||
|
||||
double scoreMultiplier = 1.0;
|
||||
|
@ -15,7 +15,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public abstract int KeyCount { get; }
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
|
||||
public override bool Ranked => true;
|
||||
|
||||
public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter)
|
||||
{
|
||||
|
@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
public override ModType Type => ModType.Conversion;
|
||||
|
||||
public override bool Ranked => false;
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
||||
{
|
||||
var maniaRuleset = (DrawableManiaRuleset)drawableRuleset;
|
||||
|
@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override string Description => "Notes are flipped horizontally.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => true;
|
||||
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
|
@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
|
||||
private void sourceChanged()
|
||||
{
|
||||
isLegacySkin = new Lazy<bool>(() => Source.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null);
|
||||
hasKeyTexture = new Lazy<bool>(() => Source.GetAnimation(
|
||||
this.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value
|
||||
?? "mania-key1", true, true) != null);
|
||||
isLegacySkin = new Lazy<bool>(() => FindProvider(s => s.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null) != null);
|
||||
hasKeyTexture = new Lazy<bool>(() => FindProvider(s => s.GetAnimation(
|
||||
s.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value
|
||||
?? "mania-key1", true, true) != null) != null);
|
||||
}
|
||||
|
||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||
|
@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
Direction.BindValueChanged(onDirectionChanged, true);
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
base.SkinChanged(skin);
|
||||
UpdateHitPosition();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
Mode: 0
|
||||
|
||||
[TimingPoints]
|
||||
0,300,4,1,2,100,1,0
|
||||
|
||||
[HitObjects]
|
||||
444,320,1000,5,0,0:0:0:0:
|
@ -1,6 +1,6 @@
|
||||
[General]
|
||||
Version: 1.0
|
||||
// no version specified means v1
|
||||
|
||||
[Fonts]
|
||||
HitCircleOverlap: 3
|
||||
ScoreOverlap: 3
|
||||
ScoreOverlap: 3
|
||||
|
@ -39,18 +39,28 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[Test]
|
||||
public void TestLegacySmoothCursorTrail()
|
||||
{
|
||||
createTest(() => new LegacySkinContainer(false)
|
||||
createTest(() =>
|
||||
{
|
||||
Child = new LegacyCursorTrail()
|
||||
var skinContainer = new LegacySkinContainer(false);
|
||||
var legacyCursorTrail = new LegacyCursorTrail(skinContainer);
|
||||
|
||||
skinContainer.Child = legacyCursorTrail;
|
||||
|
||||
return skinContainer;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLegacyDisjointCursorTrail()
|
||||
{
|
||||
createTest(() => new LegacySkinContainer(true)
|
||||
createTest(() =>
|
||||
{
|
||||
Child = new LegacyCursorTrail()
|
||||
var skinContainer = new LegacySkinContainer(true);
|
||||
var legacyCursorTrail = new LegacyCursorTrail(skinContainer);
|
||||
|
||||
skinContainer.Child = legacyCursorTrail;
|
||||
|
||||
return skinContainer;
|
||||
});
|
||||
}
|
||||
|
||||
@ -102,6 +112,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
|
||||
public event Action SourceChanged
|
||||
{
|
||||
add { }
|
||||
|
@ -113,6 +113,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => null;
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null;
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => null;
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
|
49
osu.Game.Rulesets.Osu.Tests/TestSceneOsuHitObjectSamples.cs
Normal file
49
osu.Game.Rulesets.Osu.Tests/TestSceneOsuHitObjectSamples.cs
Normal file
@ -0,0 +1,49 @@
|
||||
// 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.Reflection;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class TestSceneOsuHitObjectSamples : HitObjectSampleTest
|
||||
{
|
||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||
|
||||
protected override IResourceStore<byte[]> RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneOsuHitObjectSamples)));
|
||||
|
||||
[TestCase("normal-hitnormal")]
|
||||
[TestCase("hitnormal")]
|
||||
public void TestDefaultCustomSampleFromBeatmap(string expectedSample)
|
||||
{
|
||||
SetupSkins(expectedSample, expectedSample);
|
||||
|
||||
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
|
||||
|
||||
AssertBeatmapLookup(expectedSample);
|
||||
}
|
||||
|
||||
[TestCase("normal-hitnormal")]
|
||||
[TestCase("hitnormal")]
|
||||
public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample)
|
||||
{
|
||||
SetupSkins(string.Empty, expectedSample);
|
||||
|
||||
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
|
||||
|
||||
AssertUserLookup(expectedSample);
|
||||
}
|
||||
|
||||
[TestCase("normal-hitnormal2")]
|
||||
public void TestUserSkinLookupIgnoresSampleBank(string unwantedSample)
|
||||
{
|
||||
SetupSkins(string.Empty, unwantedSample);
|
||||
|
||||
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
|
||||
|
||||
AssertNoLookup(unwantedSample);
|
||||
}
|
||||
}
|
||||
}
|
@ -166,6 +166,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => default;
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
|
||||
public event Action SourceChanged;
|
||||
|
||||
|
@ -7,11 +7,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
{
|
||||
public class OsuDifficultyAttributes : DifficultyAttributes
|
||||
{
|
||||
public double AimStrain;
|
||||
public double SpeedStrain;
|
||||
public double ApproachRate;
|
||||
public double OverallDifficulty;
|
||||
public int HitCircleCount;
|
||||
public int SpinnerCount;
|
||||
public double AimStrain { get; set; }
|
||||
public double SpeedStrain { get; set; }
|
||||
public double ApproachRate { get; set; }
|
||||
public double OverallDifficulty { get; set; }
|
||||
public int HitCircleCount { get; set; }
|
||||
public int SpinnerCount { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -41,10 +41,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
countMeh = Score.Statistics.GetOrDefault(HitResult.Meh);
|
||||
countMiss = Score.Statistics.GetOrDefault(HitResult.Miss);
|
||||
|
||||
// Don't count scores made with supposedly unranked mods
|
||||
if (mods.Any(m => !m.Ranked))
|
||||
return 0;
|
||||
|
||||
// Custom multipliers for NoFail and SpunOut.
|
||||
double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
||||
|
||||
|
@ -27,8 +27,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override IconUsage? Icon => FontAwesome.Solid.Adjust;
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
|
||||
public override bool Ranked => false;
|
||||
|
||||
public override double ScoreMultiplier => 1.12;
|
||||
private DrawableOsuBlinds blinds;
|
||||
|
||||
|
@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public class OsuModHardRock : ModHardRock, IApplicableToHitObject
|
||||
{
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
public override bool Ranked => true;
|
||||
|
||||
public void ApplyToHitObject(HitObject hitObject)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
@ -21,7 +22,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public class OsuModRandom : ModRandom, IApplicableToBeatmap
|
||||
{
|
||||
public override string Description => "It never gets boring!";
|
||||
public override bool Ranked => false;
|
||||
|
||||
// The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle.
|
||||
// The closer the hit objects draw to the border, the sharper the turn
|
||||
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
private static readonly float border_distance_x = OsuPlayfield.BASE_SIZE.X * playfield_edge_ratio;
|
||||
private static readonly float border_distance_y = OsuPlayfield.BASE_SIZE.Y * playfield_edge_ratio;
|
||||
|
||||
private static readonly Vector2 playfield_middle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2);
|
||||
private static readonly Vector2 playfield_middle = OsuPlayfield.BASE_SIZE / 2;
|
||||
|
||||
private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast;
|
||||
|
||||
@ -74,22 +74,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
// update end position as it may have changed as a result of the position update.
|
||||
current.EndPositionRandomised = current.PositionRandomised;
|
||||
|
||||
switch (hitObject)
|
||||
{
|
||||
case Slider slider:
|
||||
// Shift nested objects the same distance as the slider got shifted in the randomisation process
|
||||
// so that moveSliderIntoPlayfield() can determine their relative distances to slider.Position and thus minMargin
|
||||
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal));
|
||||
|
||||
var oldPos = new Vector2(slider.Position.X, slider.Position.Y);
|
||||
|
||||
moveSliderIntoPlayfield(slider, current);
|
||||
|
||||
// Shift them again to move them to their final position after the slider got moved into the playfield
|
||||
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos));
|
||||
|
||||
break;
|
||||
}
|
||||
if (hitObject is Slider slider)
|
||||
moveSliderIntoPlayfield(slider, current);
|
||||
|
||||
previous = current;
|
||||
}
|
||||
@ -131,7 +117,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);
|
||||
|
||||
var position = Vector2.Add(previous.EndPositionRandomised, posRelativeToPrev);
|
||||
var position = previous.EndPositionRandomised + posRelativeToPrev;
|
||||
|
||||
// Move hit objects back into the playfield if they are outside of it,
|
||||
// which would sometimes happen during big jumps otherwise.
|
||||
@ -146,34 +132,41 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
/// </summary>
|
||||
private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo)
|
||||
{
|
||||
// Min. distances from the slider's position to the playfield border
|
||||
var minMargin = new MarginPadding();
|
||||
var minMargin = getMinSliderMargin(slider);
|
||||
|
||||
foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle))
|
||||
{
|
||||
if (!(hitObject is OsuHitObject osuHitObject))
|
||||
continue;
|
||||
|
||||
var relativePos = Vector2.Subtract(osuHitObject.Position, slider.Position);
|
||||
|
||||
minMargin.Left = Math.Max(minMargin.Left, -relativePos.X);
|
||||
minMargin.Right = Math.Max(minMargin.Right, relativePos.X);
|
||||
minMargin.Top = Math.Max(minMargin.Top, -relativePos.Y);
|
||||
minMargin.Bottom = Math.Max(minMargin.Bottom, relativePos.Y);
|
||||
}
|
||||
|
||||
if (slider.Position.X < minMargin.Left)
|
||||
slider.Position = new Vector2(minMargin.Left, slider.Position.Y);
|
||||
else if (slider.Position.X + minMargin.Right > OsuPlayfield.BASE_SIZE.X)
|
||||
slider.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - minMargin.Right, slider.Position.Y);
|
||||
|
||||
if (slider.Position.Y < minMargin.Top)
|
||||
slider.Position = new Vector2(slider.Position.X, minMargin.Top);
|
||||
else if (slider.Position.Y + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y)
|
||||
slider.Position = new Vector2(slider.Position.X, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom);
|
||||
slider.Position = new Vector2(
|
||||
Math.Clamp(slider.Position.X, minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right),
|
||||
Math.Clamp(slider.Position.Y, minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom)
|
||||
);
|
||||
|
||||
currentObjectInfo.PositionRandomised = slider.Position;
|
||||
currentObjectInfo.EndPositionRandomised = slider.EndPosition;
|
||||
|
||||
shiftNestedObjects(slider, currentObjectInfo.PositionRandomised - currentObjectInfo.PositionOriginal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the min. distances from the <see cref="Slider"/>'s position to the playfield border for the slider to be fully inside of the playfield.
|
||||
/// </summary>
|
||||
private MarginPadding getMinSliderMargin(Slider slider)
|
||||
{
|
||||
var pathPositions = new List<Vector2>();
|
||||
slider.Path.GetPathToProgress(pathPositions, 0, 1);
|
||||
|
||||
var minMargin = new MarginPadding();
|
||||
|
||||
foreach (var pos in pathPositions)
|
||||
{
|
||||
minMargin.Left = Math.Max(minMargin.Left, -pos.X);
|
||||
minMargin.Right = Math.Max(minMargin.Right, pos.X);
|
||||
minMargin.Top = Math.Max(minMargin.Top, -pos.Y);
|
||||
minMargin.Bottom = Math.Max(minMargin.Bottom, pos.Y);
|
||||
}
|
||||
|
||||
minMargin.Left = Math.Min(minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right);
|
||||
minMargin.Top = Math.Min(minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom);
|
||||
|
||||
return minMargin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -188,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
if (!(hitObject is OsuHitObject osuHitObject))
|
||||
continue;
|
||||
|
||||
osuHitObject.Position = Vector2.Add(osuHitObject.Position, shift);
|
||||
osuHitObject.Position += shift;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override ModType Type => ModType.Automation;
|
||||
public override string Description => @"Spinners will be automatically completed.";
|
||||
public override double ScoreMultiplier => 0.9;
|
||||
public override bool Ranked => true;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) };
|
||||
|
||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
|
@ -13,7 +13,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public override ModType Type => ModType.System;
|
||||
|
||||
public override bool Ranked => true;
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
while (Math.Abs(aimRotation - Arrow.Rotation) > 180)
|
||||
aimRotation += aimRotation < Arrow.Rotation ? 360 : -360;
|
||||
|
||||
if (!hasRotation)
|
||||
// The clock may be paused in a scenario like the editor.
|
||||
if (!hasRotation || !Clock.IsRunning)
|
||||
{
|
||||
Arrow.Rotation = aimRotation;
|
||||
hasRotation = true;
|
||||
|
@ -18,9 +18,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
base.SkinChanged(skin);
|
||||
updateColour();
|
||||
}
|
||||
|
||||
|
@ -128,5 +128,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
spmContainer.FadeIn(drawableSpinner.HitObject.TimeFadeIn);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (drawableSpinner != null)
|
||||
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; }
|
||||
@ -54,7 +53,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||
armedState.BindTo(drawableObject.State);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -70,19 +68,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
|
||||
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
||||
|
||||
armedState.BindValueChanged(animate, true);
|
||||
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||
updateStateTransforms(drawableObject, drawableObject.State.Value);
|
||||
}
|
||||
|
||||
private void animate(ValueChangedEvent<ArmedState> state)
|
||||
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||
{
|
||||
ClearTransforms(true);
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
||||
glow.FadeOut(400);
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
{
|
||||
switch (state.NewValue)
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
const double flash_in = 40;
|
||||
@ -109,5 +106,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (drawableObject != null)
|
||||
drawableObject.ApplyCustomUpdateState -= updateStateTransforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyCursor : OsuCursorSprite
|
||||
{
|
||||
private readonly ISkin skin;
|
||||
private bool spin;
|
||||
|
||||
public LegacyCursor()
|
||||
public LegacyCursor(ISkin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
Size = new Vector2(50);
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
@ -22,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
private void load()
|
||||
{
|
||||
bool centre = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorCentre)?.Value ?? true;
|
||||
spin = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorRotate)?.Value ?? true;
|
||||
|
@ -14,14 +14,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyCursorTrail : CursorTrail
|
||||
{
|
||||
private readonly ISkin skin;
|
||||
private const double disjoint_trail_time_separation = 1000 / 60.0;
|
||||
|
||||
private bool disjointTrail;
|
||||
private double lastTrailTime;
|
||||
private IBindable<float> cursorSize;
|
||||
|
||||
public LegacyCursorTrail(ISkin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin, OsuConfigManager config)
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
Texture = skin.GetTexture("cursortrail");
|
||||
disjointTrail = skin.GetTexture("cursormiddle") == null;
|
||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyMainCirclePiece : CompositeDrawable
|
||||
{
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
private readonly string priorityLookup;
|
||||
private readonly bool hasNumber;
|
||||
|
||||
@ -39,7 +41,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; }
|
||||
@ -114,7 +115,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||
armedState.BindTo(drawableObject.State);
|
||||
|
||||
Texture getTextureWithFallback(string name)
|
||||
{
|
||||
@ -140,18 +140,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
if (hasNumber)
|
||||
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
||||
|
||||
armedState.BindValueChanged(animate, true);
|
||||
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||
updateStateTransforms(drawableObject, drawableObject.State.Value);
|
||||
}
|
||||
|
||||
private void animate(ValueChangedEvent<ArmedState> state)
|
||||
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||
{
|
||||
const double legacy_fade_duration = 240;
|
||||
|
||||
ClearTransforms(true);
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
{
|
||||
switch (state.NewValue)
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
@ -176,5 +175,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (drawableObject != null)
|
||||
drawableObject.ApplyCustomUpdateState -= updateStateTransforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,18 +14,21 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
private readonly Drawable animationContent;
|
||||
|
||||
private readonly ISkin skin;
|
||||
|
||||
private Sprite layerNd;
|
||||
private Sprite layerSpec;
|
||||
|
||||
public LegacySliderBall(Drawable animationContent)
|
||||
public LegacySliderBall(Drawable animationContent, ISkin skin)
|
||||
{
|
||||
this.animationContent = animationContent;
|
||||
this.skin = skin;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
private void load()
|
||||
{
|
||||
var ballColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBall)?.Value ?? Color4.White;
|
||||
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
private void sourceChanged()
|
||||
{
|
||||
hasHitCircle = new Lazy<bool>(() => Source.GetTexture("hitcircle") != null);
|
||||
hasHitCircle = new Lazy<bool>(() => FindProvider(s => s.GetTexture("hitcircle") != null) != null);
|
||||
}
|
||||
|
||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||
@ -49,13 +49,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
return followCircle;
|
||||
|
||||
case OsuSkinComponents.SliderBall:
|
||||
var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: "");
|
||||
// specular and nd layers must come from the same source as the ball texure.
|
||||
var ballProvider = Source.FindProvider(s => s.GetTexture("sliderb") != null || s.GetTexture("sliderb0") != null);
|
||||
|
||||
var sliderBallContent = ballProvider.GetAnimation("sliderb", true, true, animationSeparator: "");
|
||||
|
||||
// todo: slider ball has a custom frame delay based on velocity
|
||||
// Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME);
|
||||
|
||||
if (sliderBallContent != null)
|
||||
return new LegacySliderBall(sliderBallContent);
|
||||
return new LegacySliderBall(sliderBallContent, ballProvider);
|
||||
|
||||
return null;
|
||||
|
||||
@ -84,14 +87,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.Cursor:
|
||||
if (Source.GetTexture("cursor") != null)
|
||||
return new LegacyCursor();
|
||||
var cursorProvider = Source.FindProvider(s => s.GetTexture("cursor") != null);
|
||||
|
||||
if (cursorProvider != null)
|
||||
return new LegacyCursor(cursorProvider);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorTrail:
|
||||
if (Source.GetTexture("cursortrail") != null)
|
||||
return new LegacyCursorTrail();
|
||||
var trailProvider = Source.FindProvider(s => s.GetTexture("cursortrail") != null);
|
||||
|
||||
if (trailProvider != null)
|
||||
return new LegacyCursorTrail(trailProvider);
|
||||
|
||||
return null;
|
||||
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
Size = new Vector2(size);
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
cursorExpand = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorExpand)?.Value ?? true;
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
{
|
||||
public class TaikoDifficultyAttributes : DifficultyAttributes
|
||||
{
|
||||
public double StaminaStrain;
|
||||
public double RhythmStrain;
|
||||
public double ColourStrain;
|
||||
public double ApproachRate;
|
||||
public double GreatHitWindow;
|
||||
public double StaminaStrain { get; set; }
|
||||
public double RhythmStrain { get; set; }
|
||||
public double ColourStrain { get; set; }
|
||||
public double ApproachRate { get; set; }
|
||||
public double GreatHitWindow { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -36,10 +36,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
countMeh = Score.Statistics.GetOrDefault(HitResult.Meh);
|
||||
countMiss = Score.Statistics.GetOrDefault(HitResult.Miss);
|
||||
|
||||
// Don't count scores made with supposedly unranked mods
|
||||
if (mods.Any(m => !m.Ranked))
|
||||
return 0;
|
||||
|
||||
// Custom multipliers for NoFail and SpunOut.
|
||||
double multiplier = 1.1; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
||||
|
||||
|
@ -9,7 +9,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
public class TaikoModHardRock : ModHardRock
|
||||
{
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
public override bool Ranked => true;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier factor added to the scrolling speed.
|
||||
|
@ -3,14 +3,19 @@
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class Hit : TaikoStrongableHitObject
|
||||
public class Hit : TaikoStrongableHitObject, IHasDisplayColour
|
||||
{
|
||||
public readonly Bindable<HitType> TypeBindable = new Bindable<HitType>();
|
||||
|
||||
public Bindable<Color4> DisplayColour { get; } = new Bindable<Color4>(COLOUR_CENTRE);
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="HitType"/> that actuates this <see cref="Hit"/>.
|
||||
/// </summary>
|
||||
@ -20,9 +25,17 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
set => TypeBindable.Value = value;
|
||||
}
|
||||
|
||||
public static readonly Color4 COLOUR_CENTRE = Color4Extensions.FromHex(@"bb1177");
|
||||
public static readonly Color4 COLOUR_RIM = Color4Extensions.FromHex(@"2299bb");
|
||||
|
||||
public Hit()
|
||||
{
|
||||
TypeBindable.BindValueChanged(_ => updateSamplesFromType());
|
||||
TypeBindable.BindValueChanged(_ =>
|
||||
{
|
||||
updateSamplesFromType();
|
||||
DisplayColour.Value = Type == HitType.Centre ? COLOUR_CENTRE : COLOUR_RIM;
|
||||
});
|
||||
|
||||
SamplesBindable.BindCollectionChanged((_, __) => updateTypeFromSamples());
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Skinning.Default
|
||||
@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
AccentColour = colours.PinkDarker;
|
||||
AccentColour = Hit.COLOUR_CENTRE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -21,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
AccentColour = colours.BlueDarker;
|
||||
AccentColour = Hit.COLOUR_RIM;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -152,32 +152,35 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}");
|
||||
}
|
||||
|
||||
public override ISample GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo));
|
||||
public override ISample GetSample(ISampleInfo sampleInfo)
|
||||
{
|
||||
if (sampleInfo is HitSampleInfo hitSampleInfo)
|
||||
return Source.GetSample(new LegacyTaikoSampleInfo(hitSampleInfo));
|
||||
|
||||
return base.GetSample(sampleInfo);
|
||||
}
|
||||
|
||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => Source.GetConfig<TLookup, TValue>(lookup);
|
||||
|
||||
private class LegacyTaikoSampleInfo : ISampleInfo
|
||||
private class LegacyTaikoSampleInfo : HitSampleInfo
|
||||
{
|
||||
private readonly ISampleInfo source;
|
||||
public LegacyTaikoSampleInfo(HitSampleInfo sampleInfo)
|
||||
: base(sampleInfo.Name, sampleInfo.Bank, sampleInfo.Suffix, sampleInfo.Volume)
|
||||
|
||||
public LegacyTaikoSampleInfo(ISampleInfo source)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public IEnumerable<string> LookupNames
|
||||
public override IEnumerable<string> LookupNames
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var name in source.LookupNames)
|
||||
foreach (var name in base.LookupNames)
|
||||
yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-");
|
||||
|
||||
foreach (var name in source.LookupNames)
|
||||
foreach (var name in base.LookupNames)
|
||||
yield return name;
|
||||
}
|
||||
}
|
||||
|
||||
public int Volume => source.Volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,8 +85,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
private void load(ISkinSource source)
|
||||
{
|
||||
ISkin skin = source.FindProvider(s => getAnimationFrame(s, state, 0) != null);
|
||||
|
||||
if (skin == null) return;
|
||||
|
||||
for (int frameIndex = 0; true; frameIndex++)
|
||||
{
|
||||
var texture = getAnimationFrame(skin, state, frameIndex);
|
||||
@ -112,8 +116,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
private void load(ISkinSource source)
|
||||
{
|
||||
ISkin skin = source.FindProvider(s => getAnimationFrame(s, TaikoMascotAnimationState.Clear, 0) != null);
|
||||
|
||||
if (skin == null) return;
|
||||
|
||||
foreach (var frameIndex in clear_animation_sequence)
|
||||
{
|
||||
var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex);
|
||||
|
@ -123,6 +123,8 @@ namespace osu.Game.Tests.Gameplay
|
||||
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
switch (lookup)
|
||||
|
@ -33,10 +33,11 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
||||
* outside of the range.
|
||||
*/
|
||||
|
||||
[Test]
|
||||
public void TestApplyStarQueries()
|
||||
[TestCase("star")]
|
||||
[TestCase("stars")]
|
||||
public void TestApplyStarQueries(string variant)
|
||||
{
|
||||
const string query = "stars<4 easy";
|
||||
string query = $"{variant}<4 easy";
|
||||
var filterCriteria = new FilterCriteria();
|
||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||
Assert.AreEqual("easy", filterCriteria.SearchText.Trim());
|
||||
|
@ -61,6 +61,7 @@ namespace osu.Game.Tests.NonVisual.Skinning
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotSupportedException();
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => null;
|
||||
}
|
||||
|
||||
private class TestAnimationTimeReference : IAnimationTimeReference
|
||||
|
@ -1,2 +1,2 @@
|
||||
[General]
|
||||
Version: 1.0
|
||||
// no version specified means v1
|
||||
|
116
osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs
Normal file
116
osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Skins
|
||||
{
|
||||
[TestFixture]
|
||||
[HeadlessTest]
|
||||
public class TestSceneBeatmapSkinLookupDisables : OsuTestScene
|
||||
{
|
||||
private UserSkinSource userSource;
|
||||
private BeatmapSkinSource beatmapSource;
|
||||
private SkinRequester requester;
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
Add(new SkinProvidingContainer(userSource = new UserSkinSource())
|
||||
.WithChild(new BeatmapSkinProvidingContainer(beatmapSource = new BeatmapSkinSource())
|
||||
.WithChild(requester = new SkinRequester())));
|
||||
});
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void TestDrawableLookup(bool allowBeatmapLookups)
|
||||
{
|
||||
AddStep($"Set beatmap skin enabled to {allowBeatmapLookups}", () => config.SetValue(OsuSetting.BeatmapSkins, allowBeatmapLookups));
|
||||
|
||||
string expected = allowBeatmapLookups ? "beatmap" : "user";
|
||||
|
||||
AddAssert($"Check lookup is from {expected}", () => requester.GetDrawableComponent(new TestSkinComponent())?.Name == expected);
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void TestProviderLookup(bool allowBeatmapLookups)
|
||||
{
|
||||
AddStep($"Set beatmap skin enabled to {allowBeatmapLookups}", () => config.SetValue(OsuSetting.BeatmapSkins, allowBeatmapLookups));
|
||||
|
||||
ISkin expected() => allowBeatmapLookups ? (ISkin)beatmapSource : userSource;
|
||||
|
||||
AddAssert("Check lookup is from correct source", () => requester.FindProvider(s => s.GetDrawableComponent(new TestSkinComponent()) != null) == expected());
|
||||
}
|
||||
|
||||
public class UserSkinSource : LegacySkin
|
||||
{
|
||||
public UserSkinSource()
|
||||
: base(new SkinInfo(), null, null, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
return new Container { Name = "user" };
|
||||
}
|
||||
}
|
||||
|
||||
public class BeatmapSkinSource : LegacyBeatmapSkin
|
||||
{
|
||||
public BeatmapSkinSource()
|
||||
: base(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, null, null)
|
||||
{
|
||||
}
|
||||
|
||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
return new Container { Name = "beatmap" };
|
||||
}
|
||||
}
|
||||
|
||||
public class SkinRequester : Drawable, ISkin
|
||||
{
|
||||
private ISkinSource skin;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => skin.GetDrawableComponent(component);
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => skin.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => skin.FindProvider(lookupFunction);
|
||||
}
|
||||
|
||||
private class TestSkinComponent : ISkinComponent
|
||||
{
|
||||
public string LookupName => string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -222,6 +223,8 @@ namespace osu.Game.Tests.Skins
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => skin.FindProvider(lookupFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,133 @@
|
||||
// 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.Configuration;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Background
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneBackgroundScreenDefault : OsuTestScene
|
||||
{
|
||||
private BackgroundScreenStack stack;
|
||||
private BackgroundScreenDefault screen;
|
||||
|
||||
private Graphics.Backgrounds.Background getCurrentBackground() => screen.ChildrenOfType<Graphics.Backgrounds.Background>().FirstOrDefault();
|
||||
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("create background stack", () => Child = stack = new BackgroundScreenStack());
|
||||
AddStep("push default screen", () => stack.Push(screen = new BackgroundScreenDefault(false)));
|
||||
AddUntilStep("wait for screen to load", () => screen.IsCurrentScreen());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBackgroundTypeSwitch()
|
||||
{
|
||||
setSupporter(true);
|
||||
|
||||
setSourceMode(BackgroundSource.Beatmap);
|
||||
AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground);
|
||||
|
||||
setSourceMode(BackgroundSource.BeatmapWithStoryboard);
|
||||
AddUntilStep("is storyboard background", () => getCurrentBackground() is BeatmapBackgroundWithStoryboard);
|
||||
|
||||
setSourceMode(BackgroundSource.Skin);
|
||||
AddUntilStep("is default background", () => getCurrentBackground().GetType() == typeof(Graphics.Backgrounds.Background));
|
||||
|
||||
setCustomSkin();
|
||||
AddUntilStep("is skin background", () => getCurrentBackground() is SkinBackground);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTogglingSupporterTogglesBeatmapBackground()
|
||||
{
|
||||
setSourceMode(BackgroundSource.Beatmap);
|
||||
|
||||
setSupporter(true);
|
||||
AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground);
|
||||
|
||||
setSupporter(false);
|
||||
AddUntilStep("is default background", () => !(getCurrentBackground() is BeatmapBackground));
|
||||
|
||||
setSupporter(true);
|
||||
AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground);
|
||||
}
|
||||
|
||||
[TestCase(BackgroundSource.Beatmap, typeof(BeatmapBackground))]
|
||||
[TestCase(BackgroundSource.BeatmapWithStoryboard, typeof(BeatmapBackgroundWithStoryboard))]
|
||||
[TestCase(BackgroundSource.Skin, typeof(SkinBackground))]
|
||||
public void TestBackgroundDoesntReloadOnNoChange(BackgroundSource source, Type backgroundType)
|
||||
{
|
||||
Graphics.Backgrounds.Background last = null;
|
||||
|
||||
setSourceMode(source);
|
||||
setSupporter(true);
|
||||
if (source == BackgroundSource.Skin)
|
||||
setCustomSkin();
|
||||
|
||||
AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == backgroundType);
|
||||
AddAssert("next doesn't load new background", () => screen.Next() == false);
|
||||
|
||||
// doesn't really need to be checked but might as well.
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddUntilStep("ensure same background instance", () => last == getCurrentBackground());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBackgroundCyclingOnDefaultSkin([Values] bool supporter)
|
||||
{
|
||||
Graphics.Backgrounds.Background last = null;
|
||||
|
||||
setSourceMode(BackgroundSource.Skin);
|
||||
setSupporter(supporter);
|
||||
setDefaultSkin();
|
||||
|
||||
AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == typeof(Graphics.Backgrounds.Background));
|
||||
AddAssert("next cycles background", () => screen.Next());
|
||||
|
||||
// doesn't really need to be checked but might as well.
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddUntilStep("ensure different background instance", () => last != getCurrentBackground());
|
||||
}
|
||||
|
||||
private void setSourceMode(BackgroundSource source) =>
|
||||
AddStep($"set background mode to {source}", () => config.SetValue(OsuSetting.MenuBackgroundSource, source));
|
||||
|
||||
private void setSupporter(bool isSupporter) =>
|
||||
AddStep($"set supporter {isSupporter}", () => ((DummyAPIAccess)API).LocalUser.Value = new User
|
||||
{
|
||||
IsSupporter = isSupporter,
|
||||
Id = API.LocalUser.Value.Id + 1,
|
||||
});
|
||||
|
||||
private void setCustomSkin()
|
||||
{
|
||||
// feign a skin switch. this doesn't do anything except force CurrentSkin to become a LegacySkin.
|
||||
AddStep("set custom skin", () => skins.CurrentSkinInfo.Value = new SkinInfo { ID = 5 });
|
||||
}
|
||||
|
||||
private void setDefaultSkin() => AddStep("set default skin", () => skins.CurrentSkinInfo.SetDefault());
|
||||
|
||||
[TearDownSteps]
|
||||
public void TearDown() => setDefaultSkin();
|
||||
}
|
||||
}
|
144
osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs
Normal file
144
osu.Game.Tests/Visual/Editing/TestSceneMetadataSection.cs
Normal file
@ -0,0 +1,144 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Setup;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneMetadataSection : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private EditorBeatmap editorBeatmap = new EditorBeatmap(new Beatmap());
|
||||
|
||||
private TestMetadataSection metadataSection;
|
||||
|
||||
[Test]
|
||||
public void TestMinimalMetadata()
|
||||
{
|
||||
AddStep("set metadata", () =>
|
||||
{
|
||||
editorBeatmap.Metadata.Artist = "Example Artist";
|
||||
editorBeatmap.Metadata.ArtistUnicode = null;
|
||||
|
||||
editorBeatmap.Metadata.Title = "Example Title";
|
||||
editorBeatmap.Metadata.TitleUnicode = null;
|
||||
});
|
||||
|
||||
createSection();
|
||||
|
||||
assertArtist("Example Artist");
|
||||
assertRomanisedArtist("Example Artist", false);
|
||||
|
||||
assertTitle("Example Title");
|
||||
assertRomanisedTitle("Example Title", false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInitialisationFromNonRomanisedVariant()
|
||||
{
|
||||
AddStep("set metadata", () =>
|
||||
{
|
||||
editorBeatmap.Metadata.ArtistUnicode = "*なみりん";
|
||||
editorBeatmap.Metadata.Artist = null;
|
||||
|
||||
editorBeatmap.Metadata.TitleUnicode = "コイシテイク・プラネット";
|
||||
editorBeatmap.Metadata.Title = null;
|
||||
});
|
||||
|
||||
createSection();
|
||||
|
||||
assertArtist("*なみりん");
|
||||
assertRomanisedArtist(string.Empty, true);
|
||||
|
||||
assertTitle("コイシテイク・プラネット");
|
||||
assertRomanisedTitle(string.Empty, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInitialisationPreservesOriginalValues()
|
||||
{
|
||||
AddStep("set metadata", () =>
|
||||
{
|
||||
editorBeatmap.Metadata.ArtistUnicode = "*なみりん";
|
||||
editorBeatmap.Metadata.Artist = "*namirin";
|
||||
|
||||
editorBeatmap.Metadata.TitleUnicode = "コイシテイク・プラネット";
|
||||
editorBeatmap.Metadata.Title = "Koishiteiku Planet";
|
||||
});
|
||||
|
||||
createSection();
|
||||
|
||||
assertArtist("*なみりん");
|
||||
assertRomanisedArtist("*namirin", true);
|
||||
|
||||
assertTitle("コイシテイク・プラネット");
|
||||
assertRomanisedTitle("Koishiteiku Planet", true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestValueTransfer()
|
||||
{
|
||||
AddStep("set metadata", () =>
|
||||
{
|
||||
editorBeatmap.Metadata.ArtistUnicode = "*なみりん";
|
||||
editorBeatmap.Metadata.Artist = null;
|
||||
|
||||
editorBeatmap.Metadata.TitleUnicode = "コイシテイク・プラネット";
|
||||
editorBeatmap.Metadata.Title = null;
|
||||
});
|
||||
|
||||
createSection();
|
||||
|
||||
AddStep("set romanised artist name", () => metadataSection.ArtistTextBox.Current.Value = "*namirin");
|
||||
assertArtist("*namirin");
|
||||
assertRomanisedArtist("*namirin", false);
|
||||
|
||||
AddStep("set native artist name", () => metadataSection.ArtistTextBox.Current.Value = "*なみりん");
|
||||
assertArtist("*なみりん");
|
||||
assertRomanisedArtist("*namirin", true);
|
||||
|
||||
AddStep("set romanised title", () => metadataSection.TitleTextBox.Current.Value = "Hitokoto no kyori");
|
||||
assertTitle("Hitokoto no kyori");
|
||||
assertRomanisedTitle("Hitokoto no kyori", false);
|
||||
|
||||
AddStep("set native title", () => metadataSection.TitleTextBox.Current.Value = "ヒトコトの距離");
|
||||
assertTitle("ヒトコトの距離");
|
||||
assertRomanisedTitle("Hitokoto no kyori", true);
|
||||
}
|
||||
|
||||
private void createSection()
|
||||
=> AddStep("create metadata section", () => Child = metadataSection = new TestMetadataSection());
|
||||
|
||||
private void assertArtist(string expected)
|
||||
=> AddAssert($"artist is {expected}", () => metadataSection.ArtistTextBox.Current.Value == expected);
|
||||
|
||||
private void assertRomanisedArtist(string expected, bool editable)
|
||||
{
|
||||
AddAssert($"romanised artist is {expected}", () => metadataSection.RomanisedArtistTextBox.Current.Value == expected);
|
||||
AddAssert($"romanised artist is {(editable ? "" : "not ")}editable", () => metadataSection.RomanisedArtistTextBox.ReadOnly == !editable);
|
||||
}
|
||||
|
||||
private void assertTitle(string expected)
|
||||
=> AddAssert($"title is {expected}", () => metadataSection.TitleTextBox.Current.Value == expected);
|
||||
|
||||
private void assertRomanisedTitle(string expected, bool editable)
|
||||
{
|
||||
AddAssert($"romanised title is {expected}", () => metadataSection.RomanisedTitleTextBox.Current.Value == expected);
|
||||
AddAssert($"romanised title is {(editable ? "" : "not ")}editable", () => metadataSection.RomanisedTitleTextBox.ReadOnly == !editable);
|
||||
}
|
||||
|
||||
private class TestMetadataSection : MetadataSection
|
||||
{
|
||||
public new LabelledTextBox ArtistTextBox => base.ArtistTextBox;
|
||||
public new LabelledTextBox RomanisedArtistTextBox => base.RomanisedArtistTextBox;
|
||||
|
||||
public new LabelledTextBox TitleTextBox => base.TitleTextBox;
|
||||
public new LabelledTextBox RomanisedTitleTextBox => base.RomanisedTitleTextBox;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,14 +4,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Catch.Scoring;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -29,15 +30,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneHitErrorMeter : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
[Cached(typeof(ScoreProcessor))]
|
||||
private TestScoreProcessor scoreProcessor = new TestScoreProcessor();
|
||||
|
||||
[Cached(typeof(DrawableRuleset))]
|
||||
private TestDrawableRuleset drawableRuleset = new TestDrawableRuleset();
|
||||
|
||||
public TestSceneHitErrorMeter()
|
||||
[SetUpSteps]
|
||||
public void SetUp()
|
||||
{
|
||||
recreateDisplay(new OsuHitWindows(), 5);
|
||||
AddStep("reset score processor", () => scoreProcessor.Reset());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
AddStep("create display", () => recreateDisplay(new OsuHitWindows(), 5));
|
||||
|
||||
AddRepeatStep("New random judgement", () => newJudgement(), 40);
|
||||
|
||||
@ -45,12 +53,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddRepeatStep("New max positive", () => newJudgement(drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20);
|
||||
AddStep("New fixed judgement (50ms)", () => newJudgement(50));
|
||||
|
||||
ScheduledDelegate del = null;
|
||||
AddStep("Judgement barrage", () =>
|
||||
{
|
||||
int runCount = 0;
|
||||
|
||||
ScheduledDelegate del = null;
|
||||
|
||||
del = Scheduler.AddDelayed(() =>
|
||||
{
|
||||
newJudgement(runCount++ / 10f);
|
||||
@ -60,6 +67,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
del?.Cancel();
|
||||
}, 10, true);
|
||||
});
|
||||
AddUntilStep("wait for barrage", () => del.Cancelled);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -84,10 +92,49 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCatch()
|
||||
public void TestEmpty()
|
||||
{
|
||||
AddStep("OD 1", () => recreateDisplay(new CatchHitWindows(), 1));
|
||||
AddStep("OD 10", () => recreateDisplay(new CatchHitWindows(), 10));
|
||||
AddStep("empty windows", () => recreateDisplay(HitWindows.Empty, 5));
|
||||
|
||||
AddStep("hit", () => newJudgement());
|
||||
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||
AddAssert("circle added", () =>
|
||||
this.ChildrenOfType<ColourHitErrorMeter>().All(
|
||||
meter => meter.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Count() == 1));
|
||||
|
||||
AddStep("miss", () => newJudgement(50, HitResult.Miss));
|
||||
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||
AddAssert("circle added", () =>
|
||||
this.ChildrenOfType<ColourHitErrorMeter>().All(
|
||||
meter => meter.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Count() == 2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBonus()
|
||||
{
|
||||
AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1));
|
||||
|
||||
AddStep("small bonus", () => newJudgement(result: HitResult.SmallBonus));
|
||||
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||
AddAssert("no circle added", () => !this.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Any());
|
||||
|
||||
AddStep("large bonus", () => newJudgement(result: HitResult.LargeBonus));
|
||||
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||
AddAssert("no circle added", () => !this.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIgnore()
|
||||
{
|
||||
AddStep("OD 1", () => recreateDisplay(new OsuHitWindows(), 1));
|
||||
|
||||
AddStep("ignore hit", () => newJudgement(result: HitResult.IgnoreHit));
|
||||
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||
AddAssert("no circle added", () => !this.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Any());
|
||||
|
||||
AddStep("ignore miss", () => newJudgement(result: HitResult.IgnoreMiss));
|
||||
AddAssert("no bars added", () => !this.ChildrenOfType<BarHitErrorMeter.JudgementLine>().Any());
|
||||
AddAssert("no circle added", () => !this.ChildrenOfType<ColourHitErrorMeter.HitErrorCircle>().Any());
|
||||
}
|
||||
|
||||
private void recreateDisplay(HitWindows hitWindows, float overallDifficulty)
|
||||
@ -154,12 +201,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
});
|
||||
}
|
||||
|
||||
private void newJudgement(double offset = 0)
|
||||
private void newJudgement(double offset = 0, HitResult result = HitResult.Perfect)
|
||||
{
|
||||
scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = drawableRuleset.HitWindows }, new Judgement())
|
||||
{
|
||||
TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset,
|
||||
Type = HitResult.Perfect,
|
||||
Type = result,
|
||||
});
|
||||
}
|
||||
|
||||
@ -199,5 +246,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public override void CancelResume() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class TestScoreProcessor : ScoreProcessor
|
||||
{
|
||||
public void Reset() => base.Reset(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +42,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Spacing = new Vector2(10),
|
||||
Children = new[]
|
||||
{
|
||||
new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling)
|
||||
new ExposedSkinnableDrawable("default", _ => new DefaultBox()),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox()),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.NoScaling)
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -73,9 +73,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Spacing = new Vector2(10),
|
||||
Children = new[]
|
||||
{
|
||||
new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling)
|
||||
new ExposedSkinnableDrawable("default", _ => new DefaultBox()),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.ScaleToFit),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), ConfineMode.NoScaling)
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Child = new SkinProvidingContainer(secondarySource)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)
|
||||
Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"))
|
||||
}
|
||||
};
|
||||
});
|
||||
@ -129,7 +129,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)));
|
||||
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"))));
|
||||
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
||||
AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
|
||||
}
|
||||
@ -152,7 +152,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)));
|
||||
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"))));
|
||||
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
||||
AddStep("disable", () => target.Disable());
|
||||
AddAssert("consumer using base source", () => consumer.Drawable is BaseSourceBox);
|
||||
@ -180,9 +180,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public new Drawable Drawable => base.Drawable;
|
||||
|
||||
public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null,
|
||||
ConfineMode confineMode = ConfineMode.ScaleToFit)
|
||||
: base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode)
|
||||
public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, ConfineMode confineMode = ConfineMode.ScaleToFit)
|
||||
: base(new TestSkinComponent(name), defaultImplementation, confineMode)
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -250,14 +249,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public new Drawable Drawable => base.Drawable;
|
||||
public int SkinChangedCount { get; private set; }
|
||||
|
||||
public SkinConsumer(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null)
|
||||
: base(new TestSkinComponent(name), defaultImplementation, allowFallback)
|
||||
public SkinConsumer(string name, Func<ISkinComponent, Drawable> defaultImplementation)
|
||||
: base(new TestSkinComponent(name), defaultImplementation)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
base.SkinChanged(skin);
|
||||
SkinChangedCount++;
|
||||
}
|
||||
}
|
||||
@ -301,6 +300,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class SecondarySource : ISkin
|
||||
@ -312,6 +313,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Cached(typeof(ISkinSource))]
|
||||
@ -325,6 +328,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => throw new NotImplementedException();
|
||||
|
||||
public event Action SourceChanged
|
||||
{
|
||||
add { }
|
||||
|
@ -29,14 +29,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("setup hierarchy", () =>
|
||||
{
|
||||
Children = new Drawable[]
|
||||
Child = skinSource = new TestSkinSourceContainer
|
||||
{
|
||||
skinSource = new TestSkinSourceContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = skinnableSound = new PausableSkinnableSound(new SampleInfo("Gameplay/normal-sliderslide"))
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
|
||||
// has to be added after the hierarchy above else the `ISkinSource` dependency won't be cached.
|
||||
skinSource.Add(skinnableSound = new PausableSkinnableSound(new SampleInfo("Gameplay/normal-sliderslide")));
|
||||
});
|
||||
}
|
||||
|
||||
@ -147,6 +146,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source?.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo);
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => source?.GetConfig<TLookup, TValue>(lookup);
|
||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => source?.FindProvider(lookupFunction);
|
||||
|
||||
public void TriggerSourceChanged()
|
||||
{
|
||||
|
@ -9,16 +9,19 @@ using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Overlays.Toolbar;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Options;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
using osu.Game.Tests.Visual.Multiplayer;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -152,6 +155,14 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddUntilStep("wait for track", () => !Game.MusicController.CurrentTrack.IsDummyDevice && Game.MusicController.IsPlaying);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPushSongSelectAndPressBackButtonImmediately()
|
||||
{
|
||||
AddStep("push song select", () => Game.ScreenStack.Push(new TestPlaySongSelect()));
|
||||
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action());
|
||||
AddWaitStep("wait two frames", 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitSongSelectWithClick()
|
||||
{
|
||||
@ -298,6 +309,18 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddAssert("Toolbar is hidden", () => Game.Toolbar.State.Value == Visibility.Hidden);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPushMatchSubScreenAndPressBackButtonImmediately()
|
||||
{
|
||||
TestMultiplayer multiplayer = null;
|
||||
|
||||
PushAndConfirm(() => multiplayer = new TestMultiplayer());
|
||||
|
||||
AddStep("open room", () => multiplayer.OpenNewRoom());
|
||||
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action());
|
||||
AddWaitStep("wait two frames", 2);
|
||||
}
|
||||
|
||||
private void pushEscape() =>
|
||||
AddStep("Press escape", () => InputManager.Key(Key.Escape));
|
||||
|
||||
@ -322,5 +345,18 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
protected override bool DisplayStableImportPrompt => false;
|
||||
}
|
||||
|
||||
private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
|
||||
{
|
||||
[Cached(typeof(MultiplayerClient))]
|
||||
public readonly TestMultiplayerClient Client;
|
||||
|
||||
public TestMultiplayer()
|
||||
{
|
||||
Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager);
|
||||
}
|
||||
|
||||
protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,6 +143,25 @@ Line after image";
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTableWithImageContent()
|
||||
{
|
||||
AddStep("Add Table", () =>
|
||||
{
|
||||
markdownContainer.DocumentUrl = "https://dev.ppy.sh";
|
||||
markdownContainer.Text = @"
|
||||
| Image | Name | Effect |
|
||||
| :-: | :-: | :-- |
|
||||
| ![](/wiki/Skinning/Interface/img/hit300.png ""300"") | 300 | A possible score when tapping a hit circle precisely on time, completing a Slider and keeping the cursor over every tick, or completing a Spinner with the Spinner Metre full. A score of 300 appears in an blue score by default. Scoring nothing except 300s in a beatmap will award the player with the SS or SSH grade. |
|
||||
| ![](/wiki/Skinning/Interface/img/hit300g.png ""Geki"") | (激) Geki | A term from Ouendan, called Elite Beat! in EBA. Appears when playing the last element in a combo in which the player has scored only 300s. Getting a Geki will give a sizable boost to the Life Bar. By default, it is blue. |
|
||||
| ![](/wiki/Skinning/Interface/img/hit100.png ""100"") | 100 | A possible score one can get when tapping a Hit Object slightly late or early, completing a Slider and missing a number of ticks, or completing a Spinner with the Spinner Meter almost full. A score of 100 appears in a green score by default. When very skilled players test a beatmap and they get a lot of 100s, this may mean that the beatmap does not have correct timing. |
|
||||
| ![](/wiki/Skinning/Interface/img/hit300k.png ""300 Katu"") ![](/wiki/Skinning/Interface/img/hit100k.png ""100 Katu"") | (喝) Katu or Katsu | A term from Ouendan, called Beat! in EBA. Appears when playing the last element in a combo in which the player has scored at least one 100, but no 50s or misses. Getting a Katu will give a small boost to the Life Bar. By default, it is coloured green or blue depending on whether the Katu itself is a 100 or a 300. |
|
||||
| ![](/wiki/Skinning/Interface/img/hit50.png ""50"") | 50 | A possible score one can get when tapping a hit circle rather early or late but not early or late enough to cause a miss, completing a Slider and missing a lot of ticks, or completing a Spinner with the Spinner Metre close to full. A score of 50 appears in a orange score by default. Scoring a 50 in a combo will prevent the appearance of a Katu or a Geki at the combo's end. |
|
||||
| ![](/wiki/Skinning/Interface/img/hit0.png ""Miss"") | Miss | A possible score one can get when not tapping a hit circle or too early (based on OD and AR, it may *shake* instead), not tapping or holding the Slider at least once, or completing a Spinner with low Spinner Metre fill. Scoring a Miss will reset the current combo to 0 and will prevent the appearance of a Katu or a Geki at the combo's end. |
|
||||
";
|
||||
});
|
||||
}
|
||||
|
||||
private class TestMarkdownContainer : WikiMarkdownContainer
|
||||
{
|
||||
public LinkInline Link;
|
||||
|
File diff suppressed because one or more lines are too long
65
osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs
Normal file
65
osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs
Normal file
@ -0,0 +1,65 @@
|
||||
// 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 Markdig.Parsers;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Graphics.Containers.Markdown;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Wiki;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneWikiSidebar : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Orange);
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayScrollContainer scrollContainer = new OverlayScrollContainer();
|
||||
|
||||
private WikiSidebar sidebar;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() => Child = sidebar = new WikiSidebar());
|
||||
|
||||
[Test]
|
||||
public void TestNoContent()
|
||||
{
|
||||
AddStep("No Content", () => { });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOnlyMainTitle()
|
||||
{
|
||||
AddStep("Add TOC", () =>
|
||||
{
|
||||
for (var i = 0; i < 10; i++)
|
||||
addTitle($"This is a very long title {i + 1}");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWithSubtitle()
|
||||
{
|
||||
AddStep("Add TOC", () =>
|
||||
{
|
||||
for (var i = 0; i < 10; i++)
|
||||
addTitle($"This is a very long title {i + 1}", i % 4 != 0);
|
||||
});
|
||||
}
|
||||
|
||||
private void addTitle(string text, bool subtitle = false)
|
||||
{
|
||||
var headingBlock = new HeadingBlock(new HeadingBlockParser())
|
||||
{
|
||||
Inline = new ContainerInline().AppendChild(new LiteralInline(text)),
|
||||
Level = subtitle ? 3 : 2,
|
||||
};
|
||||
var heading = new OsuMarkdownHeading(headingBlock);
|
||||
sidebar.AddEntry(headingBlock, heading);
|
||||
}
|
||||
}
|
||||
}
|
@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
}
|
||||
}
|
||||
},
|
||||
new AccuracyCircle(score)
|
||||
new AccuracyCircle(score, true)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -2,11 +2,14 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
@ -21,6 +24,45 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
[TestCase(true)]
|
||||
public void TestNonPadded(bool hasDescription) => createPaddedComponent(hasDescription, false);
|
||||
|
||||
[Test]
|
||||
public void TestFixedWidth()
|
||||
{
|
||||
const float label_width = 200;
|
||||
|
||||
AddStep("create components", () => Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new NonPaddedLabelledDrawable
|
||||
{
|
||||
Label = "short",
|
||||
FixedLabelWidth = label_width
|
||||
},
|
||||
new NonPaddedLabelledDrawable
|
||||
{
|
||||
Label = "very very very very very very very very very very very long",
|
||||
FixedLabelWidth = label_width
|
||||
},
|
||||
new PaddedLabelledDrawable
|
||||
{
|
||||
Label = "short",
|
||||
FixedLabelWidth = label_width
|
||||
},
|
||||
new PaddedLabelledDrawable
|
||||
{
|
||||
Label = "very very very very very very very very very very very long",
|
||||
FixedLabelWidth = label_width
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AddStep("unset label width", () => this.ChildrenOfType<LabelledDrawable<Drawable>>().ForEach(d => d.FixedLabelWidth = null));
|
||||
AddStep("reset label width", () => this.ChildrenOfType<LabelledDrawable<Drawable>>().ForEach(d => d.FixedLabelWidth = label_width));
|
||||
}
|
||||
|
||||
private void createPaddedComponent(bool hasDescription = false, bool padded = true)
|
||||
{
|
||||
AddStep("create component", () =>
|
||||
|
47
osu.Game/Beatmaps/MetadataUtils.cs
Normal file
47
osu.Game/Beatmaps/MetadataUtils.cs
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// Groups utility methods used to handle beatmap metadata.
|
||||
/// </summary>
|
||||
public static class MetadataUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the character <paramref name="c"/> can be used in <see cref="BeatmapMetadata.Artist"/> and <see cref="BeatmapMetadata.Title"/> fields.
|
||||
/// Characters not matched by this method can be placed in <see cref="BeatmapMetadata.ArtistUnicode"/> and <see cref="BeatmapMetadata.TitleUnicode"/>.
|
||||
/// </summary>
|
||||
public static bool IsRomanised(char c) => c <= 0xFF;
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if the string <paramref name="str"/> can be used in <see cref="BeatmapMetadata.Artist"/> and <see cref="BeatmapMetadata.Title"/> fields.
|
||||
/// Strings not matched by this method can be placed in <see cref="BeatmapMetadata.ArtistUnicode"/> and <see cref="BeatmapMetadata.TitleUnicode"/>.
|
||||
/// </summary>
|
||||
public static bool IsRomanised(string? str) => string.IsNullOrEmpty(str) || str.All(IsRomanised);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of <paramref name="str"/> with all characters that do not match <see cref="IsRomanised(char)"/> removed.
|
||||
/// </summary>
|
||||
public static string StripNonRomanisedCharacters(string? str)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return string.Empty;
|
||||
|
||||
var stringBuilder = new StringBuilder(str.Length);
|
||||
|
||||
foreach (var c in str)
|
||||
{
|
||||
if (IsRomanised(c))
|
||||
stringBuilder.Append(c);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString().Trim();
|
||||
}
|
||||
}
|
||||
}
|
@ -325,6 +325,7 @@ namespace osu.Game.Beatmaps
|
||||
public ISkin Skin => skin.Value;
|
||||
|
||||
protected abstract ISkin GetSkin();
|
||||
|
||||
private readonly RecyclableLazy<ISkin> skin;
|
||||
|
||||
public abstract Stream GetStream(string storagePath);
|
||||
|
@ -264,14 +264,18 @@ namespace osu.Game.Collections
|
||||
using (var sw = new SerializationWriter(storage.GetStream(database_name, FileAccess.Write)))
|
||||
{
|
||||
sw.Write(database_version);
|
||||
sw.Write(Collections.Count);
|
||||
|
||||
foreach (var c in Collections)
|
||||
var collectionsCopy = Collections.ToArray();
|
||||
sw.Write(collectionsCopy.Length);
|
||||
|
||||
foreach (var c in collectionsCopy)
|
||||
{
|
||||
sw.Write(c.Name.Value);
|
||||
sw.Write(c.Beatmaps.Count);
|
||||
|
||||
foreach (var b in c.Beatmaps)
|
||||
var beatmapsCopy = c.Beatmaps.ToArray();
|
||||
sw.Write(beatmapsCopy.Length);
|
||||
|
||||
foreach (var b in beatmapsCopy)
|
||||
sw.Write(b.MD5Hash);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -14,7 +15,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
/// <summary>
|
||||
/// A background which offers blurring via a <see cref="BufferedContainer"/> on demand.
|
||||
/// </summary>
|
||||
public class Background : CompositeDrawable
|
||||
public class Background : CompositeDrawable, IEquatable<Background>
|
||||
{
|
||||
private const float blur_scale = 0.5f;
|
||||
|
||||
@ -71,5 +72,14 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
|
||||
bufferedContainer?.BlurTo(newBlurSigma * blur_scale, duration, easing);
|
||||
}
|
||||
|
||||
public virtual bool Equals(Background other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
|
||||
return other.GetType() == GetType()
|
||||
&& other.textureName == textureName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,5 +24,14 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName);
|
||||
}
|
||||
|
||||
public override bool Equals(Background other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
|
||||
return other.GetType() == GetType()
|
||||
&& ((BeatmapBackground)other).Beatmap == Beatmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
osu.Game/Graphics/Backgrounds/SkinBackground.cs
Normal file
34
osu.Game/Graphics/Backgrounds/SkinBackground.cs
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
internal class SkinBackground : Background
|
||||
{
|
||||
private readonly Skin skin;
|
||||
|
||||
public SkinBackground(Skin skin, string fallbackTextureName)
|
||||
: base(fallbackTextureName)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Sprite.Texture = skin.GetTexture("menu-background") ?? Sprite.Texture;
|
||||
}
|
||||
|
||||
public override bool Equals(Background other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
|
||||
return other.GetType() == GetType()
|
||||
&& ((SkinBackground)other).skin.SkinInfo.Equals(skin.SkinInfo);
|
||||
}
|
||||
}
|
||||
}
|
@ -57,12 +57,6 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether we want to expire triangles as they exit our draw area completely.
|
||||
/// </summary>
|
||||
[Obsolete("Unused.")] // Can be removed 20210518
|
||||
protected virtual bool ExpireOffScreenTriangles => true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we should create new triangles as others expire.
|
||||
/// </summary>
|
||||
|
@ -30,9 +30,12 @@ namespace osu.Game.Graphics.Containers.Markdown
|
||||
break;
|
||||
|
||||
case ListItemBlock listItemBlock:
|
||||
var isOrdered = ((ListBlock)listItemBlock.Parent).IsOrdered;
|
||||
var childContainer = CreateListItem(listItemBlock, level, isOrdered);
|
||||
bool isOrdered = ((ListBlock)listItemBlock.Parent)?.IsOrdered == true;
|
||||
|
||||
OsuMarkdownListItem childContainer = CreateListItem(listItemBlock, level, isOrdered);
|
||||
|
||||
container.Add(childContainer);
|
||||
|
||||
foreach (var single in listItemBlock)
|
||||
base.AddMarkdownComponent(single, childContainer.Content, level);
|
||||
break;
|
||||
|
20
osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs
Normal file
20
osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using Markdig.Syntax.Inlines;
|
||||
using osu.Framework.Graphics.Containers.Markdown;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
|
||||
namespace osu.Game.Graphics.Containers.Markdown
|
||||
{
|
||||
public class OsuMarkdownImage : MarkdownImage, IHasTooltip
|
||||
{
|
||||
public string TooltipText { get; }
|
||||
|
||||
public OsuMarkdownImage(LinkInline linkInline)
|
||||
: base(linkInline.Url)
|
||||
{
|
||||
TooltipText = linkInline.Title;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user