mirror of
https://github.com/ppy/osu.git
synced 2026-05-15 18:52:50 +08:00
Compare commits
111 Commits
2020.403.0
...
2020.408.0
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RiderProjectSettingsUpdater">
|
||||
<option name="vcsConfiguration" value="1" />
|
||||
<option name="vcsConfiguration" value="2" />
|
||||
</component>
|
||||
</project>
|
||||
+1
-1
@@ -5,6 +5,6 @@
|
||||
"version": "3.1.100"
|
||||
},
|
||||
"msbuild-sdks": {
|
||||
"Microsoft.Build.Traversal": "2.0.32"
|
||||
"Microsoft.Build.Traversal": "2.0.34"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -51,7 +51,7 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.402.1" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.402.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.403.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.407.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Catch.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public abstract class CatchSkinnableTestScene : SkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(CatchRuleset),
|
||||
typeof(CatchLegacySkinTransformer),
|
||||
};
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new CatchRuleset();
|
||||
}
|
||||
}
|
||||
@@ -4,21 +4,21 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneCatcher : SkinnableTestScene
|
||||
public class TestSceneCatcher : CatchSkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
||||
{
|
||||
typeof(CatcherArea),
|
||||
typeof(CatcherSprite)
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
||||
@@ -17,12 +17,11 @@ using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneCatcherArea : SkinnableTestScene
|
||||
public class TestSceneCatcherArea : CatchSkinnableTestScene
|
||||
{
|
||||
private RulesetInfo catchRuleset;
|
||||
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneFruitObjects : SkinnableTestScene
|
||||
public class TestSceneFruitObjects : CatchSkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
||||
{
|
||||
typeof(CatchHitObject),
|
||||
typeof(Fruit),
|
||||
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
typeof(DrawableBanana),
|
||||
typeof(DrawableBananaShower),
|
||||
typeof(Pulp),
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
|
||||
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new CatchLegacySkinTransformer(source);
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new CatchLegacySkinTransformer(source);
|
||||
|
||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new CatchPerformanceCalculator(this, beatmap, score);
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
}
|
||||
|
||||
currentCatcher.Show();
|
||||
(currentCatcher.Drawable as IAnimation)?.GotoFrame(0);
|
||||
(currentCatcher.Drawable as IFramedAnimation)?.GotoFrame(0);
|
||||
}
|
||||
|
||||
private void beginTrail()
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Mania.Skinning;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Rulesets.UI.Scrolling.Algorithms;
|
||||
using osu.Game.Tests.Visual;
|
||||
@@ -24,6 +27,14 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[Cached(Type = typeof(IScrollingInfo))]
|
||||
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(ManiaRuleset),
|
||||
typeof(ManiaLegacySkinTransformer),
|
||||
};
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new ManiaRuleset();
|
||||
|
||||
protected ManiaSkinnableTestScene()
|
||||
{
|
||||
scrollingInfo.Direction.Value = ScrollingDirection.Down;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 0), _ => new DefaultColumnBackground())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, 0), _ => new DefaultColumnBackground())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new ColumnHitObjectArea(new HitObjectContainer())
|
||||
Child = new ColumnHitObjectArea(0, new HitObjectContainer())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new ColumnHitObjectArea(new HitObjectContainer())
|
||||
Child = new ColumnHitObjectArea(1, new HitObjectContainer())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
|
||||
@@ -10,11 +10,10 @@ using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
public class TestSceneDrawableJudgement : SkinnableTestScene
|
||||
public class TestSceneDrawableJudgement : ManiaSkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
|
||||
CreatedDrawables.OfType<Container>().ForEach(c =>
|
||||
{
|
||||
c.Add(new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion),
|
||||
c.Add(new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, 0),
|
||||
_ => new DefaultHitExplosion((runcount / 15) % 2 == 0 ? new Color4(94, 0, 57, 255) : new Color4(6, 84, 0, 255), runcount % 6 != 0)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, 0), _ => new DefaultKeyArea())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
|
||||
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, 1), _ => new DefaultKeyArea())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
public class TestScenePlayfield : ManiaSkinnableTestScene
|
||||
{
|
||||
private List<StageDefinition> stageDefinitions = new List<StageDefinition>();
|
||||
|
||||
[Test]
|
||||
public void TestSingleStage()
|
||||
{
|
||||
AddStep("create stage", () =>
|
||||
{
|
||||
stageDefinitions = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 2 }
|
||||
};
|
||||
|
||||
SetContents(() => new ManiaPlayfield(stageDefinitions));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDualStages()
|
||||
{
|
||||
AddStep("create stage", () =>
|
||||
{
|
||||
stageDefinitions = new List<StageDefinition>
|
||||
{
|
||||
new StageDefinition { Columns = 2 },
|
||||
new StageDefinition { Columns = 2 }
|
||||
};
|
||||
|
||||
SetContents(() => new ManiaPlayfield(stageDefinitions));
|
||||
});
|
||||
}
|
||||
|
||||
protected override IBeatmap CreateBeatmapForSkinProvider()
|
||||
{
|
||||
var maniaBeatmap = (ManiaBeatmap)base.CreateBeatmapForSkinProvider();
|
||||
maniaBeatmap.Stages = stageDefinitions;
|
||||
return maniaBeatmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Configuration.Tracking;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
@@ -19,13 +20,14 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
||||
{
|
||||
base.InitialiseDefaults();
|
||||
|
||||
Set(ManiaRulesetSetting.ScrollTime, 1500.0, 50.0, 5000.0, 50.0);
|
||||
Set(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 1);
|
||||
Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||
}
|
||||
|
||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||
{
|
||||
new TrackedSetting<double>(ManiaRulesetSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
|
||||
new TrackedSetting<double>(ManiaRulesetSetting.ScrollTime,
|
||||
v => new SettingDescription(v, "Scroll Speed", $"{(int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / v)} ({v}ms)"))
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this);
|
||||
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new ManiaLegacySkinTransformer(source);
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new ManiaLegacySkinTransformer(source, beatmap);
|
||||
|
||||
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
|
||||
{
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
// 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.Mania.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
public class ManiaSkinComponent : GameplaySkinComponent<ManiaSkinComponents>
|
||||
{
|
||||
public ManiaSkinComponent(ManiaSkinComponents component)
|
||||
/// <summary>
|
||||
/// The intended <see cref="Column"/> index for this component.
|
||||
/// May be null if the component does not exist in a <see cref="Column"/>.
|
||||
/// </summary>
|
||||
public readonly int? TargetColumn;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ManiaSkinComponent"/>.
|
||||
/// </summary>
|
||||
/// <param name="component">The component.</param>
|
||||
/// <param name="targetColumn">The intended <see cref="Column"/> index for this component. May be null if the component does not exist in a <see cref="Column"/>.</param>
|
||||
public ManiaSkinComponent(ManiaSkinComponents component, int? targetColumn = null)
|
||||
: base(component)
|
||||
{
|
||||
TargetColumn = targetColumn;
|
||||
}
|
||||
|
||||
protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME;
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
AddRangeInternal(new[]
|
||||
{
|
||||
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody), _ => new DefaultBodyPiece())
|
||||
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece())
|
||||
{
|
||||
RelativeSizeAxes = Axes.X
|
||||
},
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
AddInternal(headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component), _ => new DefaultNotePiece())
|
||||
AddInternal(headPiece = new SkinnableDrawable(new ManiaSkinComponent(Component, hitObject.Column), _ => new DefaultNotePiece())
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
|
||||
@@ -6,7 +6,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
@@ -21,12 +20,6 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
|
||||
private Drawable sprite;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private ManiaStage stage { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private Column column { get; set; }
|
||||
|
||||
public LegacyBodyPiece()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@@ -18,12 +18,14 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
public class LegacyColumnBackground : LegacyManiaColumnElement, IKeyBindingHandler<ManiaAction>
|
||||
{
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
private readonly bool isLastColumn;
|
||||
|
||||
private Container lightContainer;
|
||||
private Sprite light;
|
||||
|
||||
public LegacyColumnBackground()
|
||||
public LegacyColumnBackground(bool isLastColumn)
|
||||
{
|
||||
this.isLastColumn = isLastColumn;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
@@ -40,21 +42,26 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
|
||||
bool hasLeftLine = leftLineWidth > 0;
|
||||
bool hasRightLine = rightLineWidth > 0 && skin.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.4m
|
||||
|| Stage == null || Column.Index == Stage.Columns.Count - 1;
|
||||
|| isLastColumn;
|
||||
|
||||
float lightPosition = skin.GetConfig<LegacyManiaSkinConfigurationLookup, float>(
|
||||
new LegacyManiaSkinConfigurationLookup(Stage?.Columns.Count ?? 4, LegacyManiaSkinConfigurationLookups.LightPosition))?.Value
|
||||
float lightPosition = GetManiaSkinConfig<float>(skin, LegacyManiaSkinConfigurationLookups.LightPosition)?.Value
|
||||
?? 0;
|
||||
|
||||
Color4 lineColour = GetManiaSkinConfig<Color4>(skin, LegacyManiaSkinConfigurationLookups.ColumnLineColour)?.Value
|
||||
?? Color4.White;
|
||||
|
||||
Color4 backgroundColour = GetManiaSkinConfig<Color4>(skin, LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour)?.Value
|
||||
?? Color4.Black;
|
||||
|
||||
Color4 lightColour = GetManiaSkinConfig<Color4>(skin, LegacyManiaSkinConfigurationLookups.ColumnLightColour)?.Value
|
||||
?? Color4.White;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black
|
||||
Colour = backgroundColour
|
||||
},
|
||||
new Box
|
||||
{
|
||||
@@ -81,6 +88,7 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Colour = lightColour,
|
||||
Texture = skin.GetTexture(lightImage),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 1,
|
||||
|
||||
@@ -36,10 +36,10 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
// This animation is discarded and re-queried with the appropriate frame length afterwards.
|
||||
var tmp = skin.GetAnimation(imageName, true, false);
|
||||
double frameLength = 0;
|
||||
if (tmp is IAnimation tmpAnimation && tmpAnimation.FrameCount > 0)
|
||||
if (tmp is IFramedAnimation tmpAnimation && tmpAnimation.FrameCount > 0)
|
||||
frameLength = Math.Max(1000 / 60.0, 170.0 / tmpAnimation.FrameCount);
|
||||
|
||||
explosion = skin.GetAnimation(imageName, true, false, startAtCurrentTime: true, frameLength: frameLength).With(d =>
|
||||
explosion = skin.GetAnimation(imageName, true, false, frameLength: frameLength).With(d =>
|
||||
{
|
||||
if (d == null)
|
||||
return;
|
||||
|
||||
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning
|
||||
{
|
||||
@@ -33,6 +34,9 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
bool showJudgementLine = GetManiaSkinConfig<bool>(skin, LegacyManiaSkinConfigurationLookups.ShowJudgementLine)?.Value
|
||||
?? true;
|
||||
|
||||
Color4 lineColour = GetManiaSkinConfig<Color4>(skin, LegacyManiaSkinConfigurationLookups.JudgementLineColour)?.Value
|
||||
?? Color4.White;
|
||||
|
||||
InternalChild = directionContainer = new Container
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
@@ -52,6 +56,7 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
Anchor = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 1,
|
||||
Colour = lineColour,
|
||||
Alpha = showJudgementLine ? 0.9f : 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,6 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
private Sprite upSprite;
|
||||
private Sprite downSprite;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private ManiaStage stage { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private Column column { get; set; }
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning
|
||||
@@ -15,10 +12,6 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
/// </summary>
|
||||
public class LegacyManiaElement : CompositeDrawable
|
||||
{
|
||||
[Resolved(CanBeNull = true)]
|
||||
[CanBeNull]
|
||||
protected ManiaStage Stage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a per-column-count skin configuration.
|
||||
/// </summary>
|
||||
@@ -26,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
/// <param name="lookup">The value to retrieve.</param>
|
||||
/// <param name="index">If not null, denotes the index of the column to which the entry applies.</param>
|
||||
protected virtual IBindable<T> GetManiaSkinConfig<T>(ISkin skin, LegacyManiaSkinConfigurationLookups lookup, int? index = null)
|
||||
=> skin.GetConfig<LegacyManiaSkinConfigurationLookup, T>(
|
||||
new LegacyManiaSkinConfigurationLookup(Stage?.Columns.Count ?? 4, lookup, index));
|
||||
=> skin.GetConfig<ManiaSkinConfigurationLookup, T>(
|
||||
new ManiaSkinConfigurationLookup(lookup, index));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
private Container directionContainer;
|
||||
private Sprite noteSprite;
|
||||
|
||||
private float? minimumColumnWidth;
|
||||
|
||||
public LegacyNotePiece()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@@ -29,6 +31,8 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
|
||||
{
|
||||
minimumColumnWidth = skin.GetConfig<ManiaSkinConfigurationLookup, float>(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.MinimumColumnWidth))?.Value;
|
||||
|
||||
InternalChild = directionContainer = new Container
|
||||
{
|
||||
Origin = Anchor.BottomCentre,
|
||||
@@ -47,8 +51,10 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
|
||||
if (noteSprite.Texture != null)
|
||||
{
|
||||
var scale = DrawWidth / noteSprite.Texture.DisplayWidth;
|
||||
noteSprite.Scale = new Vector2(scale);
|
||||
// The height is scaled to the minimum column width, if provided.
|
||||
float minimumWidth = minimumColumnWidth ?? DrawWidth;
|
||||
|
||||
noteSprite.Scale = Vector2.Divide(new Vector2(DrawWidth, minimumWidth), noteSprite.Texture.DisplayWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning
|
||||
@@ -15,6 +17,7 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
public class ManiaLegacySkinTransformer : ISkin
|
||||
{
|
||||
private readonly ISkin source;
|
||||
private readonly ManiaBeatmap beatmap;
|
||||
|
||||
private Lazy<bool> isLegacySkin;
|
||||
|
||||
@@ -24,9 +27,10 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
/// </summary>
|
||||
private Lazy<bool> hasKeyTexture;
|
||||
|
||||
public ManiaLegacySkinTransformer(ISkinSource source)
|
||||
public ManiaLegacySkinTransformer(ISkinSource source, IBeatmap beatmap)
|
||||
{
|
||||
this.source = source;
|
||||
this.beatmap = (ManiaBeatmap)beatmap;
|
||||
|
||||
source.SourceChanged += sourceChanged;
|
||||
sourceChanged();
|
||||
@@ -36,8 +40,8 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
{
|
||||
isLegacySkin = new Lazy<bool>(() => source.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null);
|
||||
hasKeyTexture = new Lazy<bool>(() => source.GetAnimation(
|
||||
source.GetConfig<LegacyManiaSkinConfigurationLookup, string>(
|
||||
new LegacyManiaSkinConfigurationLookup(4, LegacyManiaSkinConfigurationLookups.KeyImage, 0))?.Value
|
||||
GetConfig<ManiaSkinConfigurationLookup, string>(
|
||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.KeyImage, 0))?.Value
|
||||
?? "mania-key1", true, true) != null);
|
||||
}
|
||||
|
||||
@@ -55,7 +59,7 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
switch (maniaComponent.Component)
|
||||
{
|
||||
case ManiaSkinComponents.ColumnBackground:
|
||||
return new LegacyColumnBackground();
|
||||
return new LegacyColumnBackground(maniaComponent.TargetColumn == beatmap.TotalColumns - 1);
|
||||
|
||||
case ManiaSkinComponents.HitTarget:
|
||||
return new LegacyHitTarget();
|
||||
@@ -115,7 +119,12 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample);
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) =>
|
||||
source.GetConfig<TLookup, TValue>(lookup);
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
if (lookup is ManiaSkinConfigurationLookup maniaLookup)
|
||||
return source.GetConfig<LegacyManiaSkinConfigurationLookup, TValue>(new LegacyManiaSkinConfigurationLookup(beatmap.TotalColumns, maniaLookup.Lookup, maniaLookup.TargetColumn));
|
||||
|
||||
return source.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning
|
||||
{
|
||||
public class ManiaSkinConfigurationLookup
|
||||
{
|
||||
/// <summary>
|
||||
/// The configuration lookup value.
|
||||
/// </summary>
|
||||
public readonly LegacyManiaSkinConfigurationLookups Lookup;
|
||||
|
||||
/// <summary>
|
||||
/// The intended <see cref="Column"/> index for the configuration.
|
||||
/// May be null if the configuration does not apply to a <see cref="Column"/>.
|
||||
/// </summary>
|
||||
public readonly int? TargetColumn;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ManiaSkinConfigurationLookup"/>.
|
||||
/// </summary>
|
||||
/// <param name="lookup">The lookup value.</param>
|
||||
/// <param name="targetColumn">The intended <see cref="Column"/> index for the configuration. May be null if the configuration does not apply to a <see cref="Column"/>.</param>
|
||||
public ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups lookup, int? targetColumn = null)
|
||||
{
|
||||
Lookup = lookup;
|
||||
TargetColumn = targetColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
|
||||
Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
|
||||
Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground, Index), _ => new DefaultColumnBackground())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
@@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
|
||||
background.CreateProxy(),
|
||||
hitObjectArea = new ColumnHitObjectArea(HitObjectContainer) { RelativeSizeAxes = Axes.Both },
|
||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea), _ => new DefaultKeyArea())
|
||||
hitObjectArea = new ColumnHitObjectArea(Index, HitObjectContainer) { RelativeSizeAxes = Axes.Both },
|
||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.KeyArea, Index), _ => new DefaultKeyArea())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
@@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value)
|
||||
return;
|
||||
|
||||
var explosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion), _ =>
|
||||
var explosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, Index), _ =>
|
||||
new DefaultHitExplosion(judgedObject.AccentColour.Value, judgedObject is DrawableHoldNoteTick))
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
|
||||
@@ -14,12 +14,12 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
public readonly Container Explosions;
|
||||
private readonly Drawable hitTarget;
|
||||
|
||||
public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
|
||||
public ColumnHitObjectArea(int columnIndex, HitObjectContainer hitObjectContainer)
|
||||
: base(hitObjectContainer)
|
||||
{
|
||||
AddRangeInternal(new[]
|
||||
{
|
||||
hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget), _ => new DefaultHitTarget())
|
||||
hitTarget = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitTarget, columnIndex), _ => new DefaultHitTarget())
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Depth = 1
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Skinning;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
@@ -14,9 +15,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
{
|
||||
protected readonly IBindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private ManiaStage stage { get; set; }
|
||||
|
||||
public HitObjectArea(HitObjectContainer hitObjectContainer)
|
||||
{
|
||||
InternalChildren = new[]
|
||||
@@ -45,8 +43,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
|
||||
protected virtual void UpdateHitPosition()
|
||||
{
|
||||
float hitPosition = CurrentSkin.GetConfig<LegacyManiaSkinConfigurationLookup, float>(
|
||||
new LegacyManiaSkinConfigurationLookup(stage?.Columns.Count ?? 4, LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
|
||||
float hitPosition = CurrentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
|
||||
?? ManiaStage.HIT_TARGET_POSITION;
|
||||
|
||||
Padding = Direction.Value == ScrollingDirection.Up
|
||||
|
||||
@@ -5,8 +5,10 @@ using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
@@ -25,6 +27,16 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public class DrawableManiaRuleset : DrawableScrollingRuleset<ManiaHitObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// The minimum time range. This occurs at a <see cref="relativeTimeRange"/> of 40.
|
||||
/// </summary>
|
||||
public const double MIN_TIME_RANGE = 150;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum time range. This occurs at a <see cref="relativeTimeRange"/> of 1.
|
||||
/// </summary>
|
||||
public const double MAX_TIME_RANGE = 6000;
|
||||
|
||||
protected new ManiaPlayfield Playfield => (ManiaPlayfield)base.Playfield;
|
||||
|
||||
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
|
||||
@@ -46,6 +58,19 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
bool isForCurrentRuleset = Beatmap.BeatmapInfo.Ruleset.Equals(Ruleset.RulesetInfo);
|
||||
|
||||
foreach (var p in ControlPoints)
|
||||
{
|
||||
// Mania doesn't care about global velocity
|
||||
p.Velocity = 1;
|
||||
p.BaseBeatLength *= Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier;
|
||||
|
||||
// For non-mania beatmap, speed changes should only happen through timing points
|
||||
if (!isForCurrentRuleset)
|
||||
p.DifficultyPoint = new DifficultyControlPoint();
|
||||
}
|
||||
|
||||
BarLines.ForEach(Playfield.Add);
|
||||
|
||||
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
|
||||
@@ -54,6 +79,17 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange);
|
||||
}
|
||||
|
||||
protected override void AdjustScrollSpeed(int amount)
|
||||
{
|
||||
this.TransformTo(nameof(relativeTimeRange), relativeTimeRange + amount, 200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private double relativeTimeRange
|
||||
{
|
||||
get => MAX_TIME_RANGE / TimeRange.Value;
|
||||
set => TimeRange.Value = MAX_TIME_RANGE / value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the column that intersects a screen-space position.
|
||||
/// </summary>
|
||||
|
||||
@@ -11,6 +11,7 @@ using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Skinning;
|
||||
using osu.Game.Rulesets.Mania.UI.Components;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@@ -24,7 +25,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
/// <summary>
|
||||
/// A collection of <see cref="Column"/>s.
|
||||
/// </summary>
|
||||
[Cached]
|
||||
public class ManiaStage : ScrollingPlayfield
|
||||
{
|
||||
public const float COLUMN_SPACING = 1;
|
||||
@@ -146,15 +146,15 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
if (col.Index > 0)
|
||||
{
|
||||
float spacing = currentSkin.GetConfig<LegacyManiaSkinConfigurationLookup, float>(
|
||||
new LegacyManiaSkinConfigurationLookup(Columns.Count, LegacyManiaSkinConfigurationLookups.ColumnSpacing, col.Index - 1))
|
||||
float spacing = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnSpacing, col.Index - 1))
|
||||
?.Value ?? COLUMN_SPACING;
|
||||
|
||||
col.Margin = new MarginPadding { Left = spacing };
|
||||
}
|
||||
|
||||
float? width = currentSkin.GetConfig<LegacyManiaSkinConfigurationLookup, float>(
|
||||
new LegacyManiaSkinConfigurationLookup(Columns.Count, LegacyManiaSkinConfigurationLookups.ColumnWidth, col.Index))
|
||||
float? width = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, col.Index))
|
||||
?.Value;
|
||||
|
||||
if (width == null)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Osu.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public abstract class OsuSkinnableTestScene : SkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(OsuRuleset),
|
||||
typeof(OsuLegacySkinTransformer),
|
||||
};
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
}
|
||||
}
|
||||
@@ -10,17 +10,16 @@ using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class TestSceneDrawableJudgement : SkinnableTestScene
|
||||
public class TestSceneDrawableJudgement : OsuSkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
||||
{
|
||||
typeof(DrawableJudgement),
|
||||
typeof(DrawableOsuJudgement)
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
public TestSceneDrawableJudgement()
|
||||
{
|
||||
|
||||
@@ -3,26 +3,32 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing.Input;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Osu.Skinning;
|
||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneGameplayCursor : SkinnableTestScene
|
||||
public class TestSceneGameplayCursor : OsuSkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
||||
{
|
||||
typeof(GameplayCursorContainer),
|
||||
typeof(OsuCursorContainer),
|
||||
typeof(OsuCursor),
|
||||
typeof(LegacyCursor),
|
||||
typeof(LegacyCursorTrail),
|
||||
typeof(CursorTrail)
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
[Cached]
|
||||
private GameplayBeatmap gameplayBeatmap;
|
||||
|
||||
@@ -14,12 +14,11 @@ using osu.Game.Rulesets.Mods;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneHitCircle : SkinnableTestScene
|
||||
public class TestSceneHitCircle : OsuSkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
|
||||
@@ -22,12 +22,11 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneSlider : SkinnableTestScene
|
||||
public class TestSceneSlider : OsuSkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
// 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 Humanizer;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Configuration;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using osu.Game.Storyboards;
|
||||
using osuTK;
|
||||
using static osu.Game.Tests.Visual.OsuTestScene.ClockBackedTestWorkingBeatmap;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneSliderSnaking : TestSceneOsuPlayer
|
||||
{
|
||||
[Resolved]
|
||||
private AudioManager audioManager { get; set; }
|
||||
|
||||
private TrackVirtualManual track;
|
||||
|
||||
protected override bool Autoplay => autoplay;
|
||||
private bool autoplay;
|
||||
|
||||
private readonly BindableBool snakingIn = new BindableBool();
|
||||
private readonly BindableBool snakingOut = new BindableBool();
|
||||
|
||||
private const double duration_of_span = 3605;
|
||||
private const double fade_in_modifier = -1200;
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
{
|
||||
var working = new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||
track = (TrackVirtualManual)working.Track;
|
||||
return working;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetConfigCache configCache)
|
||||
{
|
||||
var config = (OsuRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
||||
config.BindWith(OsuRulesetSetting.SnakingInSliders, snakingIn);
|
||||
config.BindWith(OsuRulesetSetting.SnakingOutSliders, snakingOut);
|
||||
}
|
||||
|
||||
private DrawableSlider slider;
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps() { }
|
||||
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
public void TestSnakingEnabled(int sliderIndex)
|
||||
{
|
||||
AddStep("enable autoplay", () => autoplay = true);
|
||||
base.SetUpSteps();
|
||||
AddUntilStep("wait for track to start running", () => track.IsRunning);
|
||||
|
||||
double startTime = hitObjects[sliderIndex].StartTime;
|
||||
retrieveDrawableSlider(sliderIndex);
|
||||
setSnaking(true);
|
||||
|
||||
ensureSnakingIn(startTime + fade_in_modifier);
|
||||
|
||||
for (int i = 0; i < sliderIndex; i++)
|
||||
{
|
||||
// non-final repeats should not snake out
|
||||
ensureNoSnakingOut(startTime, i);
|
||||
}
|
||||
|
||||
// final repeat should snake out
|
||||
ensureSnakingOut(startTime, sliderIndex);
|
||||
}
|
||||
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
public void TestSnakingDisabled(int sliderIndex)
|
||||
{
|
||||
AddStep("have autoplay", () => autoplay = true);
|
||||
base.SetUpSteps();
|
||||
AddUntilStep("wait for track to start running", () => track.IsRunning);
|
||||
|
||||
double startTime = hitObjects[sliderIndex].StartTime;
|
||||
retrieveDrawableSlider(sliderIndex);
|
||||
setSnaking(false);
|
||||
|
||||
ensureNoSnakingIn(startTime + fade_in_modifier);
|
||||
|
||||
for (int i = 0; i <= sliderIndex; i++)
|
||||
{
|
||||
// no snaking out ever, including final repeat
|
||||
ensureNoSnakingOut(startTime, i);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRepeatArrowDoesNotMoveWhenHit()
|
||||
{
|
||||
AddStep("enable autoplay", () => autoplay = true);
|
||||
setSnaking(true);
|
||||
base.SetUpSteps();
|
||||
|
||||
// repeat might have a chance to update its position depending on where in the frame its hit,
|
||||
// so some leniency is allowed here instead of checking strict equality
|
||||
checkPositionChange(16600, sliderRepeat, positionAlmostSame);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRepeatArrowMovesWhenNotHit()
|
||||
{
|
||||
AddStep("disable autoplay", () => autoplay = false);
|
||||
setSnaking(true);
|
||||
base.SetUpSteps();
|
||||
|
||||
checkPositionChange(16600, sliderRepeat, positionDecreased);
|
||||
}
|
||||
|
||||
private void retrieveDrawableSlider(int index) => AddStep($"retrieve {(index + 1).ToOrdinalWords()} slider", () =>
|
||||
{
|
||||
slider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(index);
|
||||
});
|
||||
|
||||
private void ensureSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionIncreased);
|
||||
private void ensureNoSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionRemainsSame);
|
||||
|
||||
private void ensureSnakingOut(double startTime, int repeatIndex)
|
||||
{
|
||||
var repeatTime = timeAtRepeat(startTime, repeatIndex);
|
||||
|
||||
if (repeatIndex % 2 == 0)
|
||||
checkPositionChange(repeatTime, sliderStart, positionIncreased);
|
||||
else
|
||||
checkPositionChange(repeatTime, sliderEnd, positionDecreased);
|
||||
}
|
||||
|
||||
private void ensureNoSnakingOut(double startTime, int repeatIndex) =>
|
||||
checkPositionChange(timeAtRepeat(startTime, repeatIndex), positionAtRepeat(repeatIndex), positionRemainsSame);
|
||||
|
||||
private double timeAtRepeat(double startTime, int repeatIndex) => startTime + 100 + duration_of_span * repeatIndex;
|
||||
private Func<Vector2> positionAtRepeat(int repeatIndex) => repeatIndex % 2 == 0 ? (Func<Vector2>)sliderStart : sliderEnd;
|
||||
|
||||
private List<Vector2> sliderCurve => ((PlaySliderBody)slider.Body.Drawable).CurrentCurve;
|
||||
private Vector2 sliderStart() => sliderCurve.First();
|
||||
private Vector2 sliderEnd() => sliderCurve.Last();
|
||||
|
||||
private Vector2 sliderRepeat()
|
||||
{
|
||||
var drawable = Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(1);
|
||||
var repeat = drawable.ChildrenOfType<Container<DrawableSliderRepeat>>().First().Children.First();
|
||||
return repeat.Position;
|
||||
}
|
||||
|
||||
private bool positionRemainsSame(Vector2 previous, Vector2 current) => previous == current;
|
||||
private bool positionIncreased(Vector2 previous, Vector2 current) => current.X > previous.X && current.Y > previous.Y;
|
||||
private bool positionDecreased(Vector2 previous, Vector2 current) => current.X < previous.X && current.Y < previous.Y;
|
||||
private bool positionAlmostSame(Vector2 previous, Vector2 current) => Precision.AlmostEquals(previous, current, 1);
|
||||
|
||||
private void checkPositionChange(double startTime, Func<Vector2> positionToCheck, Func<Vector2, Vector2, bool> positionAssertion)
|
||||
{
|
||||
Vector2 previousPosition = Vector2.Zero;
|
||||
|
||||
string positionDescription = positionToCheck.Method.Name.Humanize(LetterCasing.LowerCase);
|
||||
string assertionDescription = positionAssertion.Method.Name.Humanize(LetterCasing.LowerCase);
|
||||
|
||||
addSeekStep(startTime);
|
||||
AddStep($"save {positionDescription} position", () => previousPosition = positionToCheck.Invoke());
|
||||
addSeekStep(startTime + 100);
|
||||
AddAssert($"{positionDescription} {assertionDescription}", () =>
|
||||
{
|
||||
var currentPosition = positionToCheck.Invoke();
|
||||
return positionAssertion.Invoke(previousPosition, currentPosition);
|
||||
});
|
||||
}
|
||||
|
||||
private void setSnaking(bool value)
|
||||
{
|
||||
AddStep($"{(value ? "enable" : "disable")} snaking", () =>
|
||||
{
|
||||
snakingIn.Value = value;
|
||||
snakingOut.Value = value;
|
||||
});
|
||||
}
|
||||
|
||||
private void addSeekStep(double time)
|
||||
{
|
||||
AddStep($"seek to {time}", () => track.Seek(time));
|
||||
|
||||
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
|
||||
}
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap
|
||||
{
|
||||
HitObjects = hitObjects
|
||||
};
|
||||
|
||||
private readonly List<HitObject> hitObjects = new List<HitObject>
|
||||
{
|
||||
new Slider
|
||||
{
|
||||
StartTime = 3000,
|
||||
Position = new Vector2(100, 100),
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(300, 200)
|
||||
}),
|
||||
},
|
||||
new Slider
|
||||
{
|
||||
StartTime = 13000,
|
||||
Position = new Vector2(100, 100),
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(300, 200)
|
||||
}),
|
||||
RepeatCount = 1,
|
||||
},
|
||||
new Slider
|
||||
{
|
||||
StartTime = 23000,
|
||||
Position = new Vector2(100, 100),
|
||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(300, 200)
|
||||
}),
|
||||
RepeatCount = 2,
|
||||
},
|
||||
new HitCircle
|
||||
{
|
||||
StartTime = 199999,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
Anchor = Anchor.Centre,
|
||||
Alpha = 0.5f,
|
||||
}
|
||||
}, confineMode: ConfineMode.NoScaling);
|
||||
});
|
||||
}
|
||||
|
||||
public double AnimationStartTime { get; set; }
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
base.ApplySkin(skin, allowFallback);
|
||||
|
||||
bool allowBallTint = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false;
|
||||
Ball.Colour = allowBallTint ? AccentColour.Value : Color4.White;
|
||||
Ball.AccentColour = allowBallTint ? AccentColour.Value : Color4.White;
|
||||
}
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this);
|
||||
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkinTransformer(source);
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new OsuLegacySkinTransformer(source);
|
||||
|
||||
public int LegacyID => 0;
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
switch (osuComponent.Component)
|
||||
{
|
||||
case OsuSkinComponents.FollowPoint:
|
||||
return this.GetAnimation(component.LookupName, true, false, true);
|
||||
return this.GetAnimation(component.LookupName, true, false, true, startAtCurrentTime: false);
|
||||
|
||||
case OsuSkinComponents.SliderFollowCircle:
|
||||
var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true);
|
||||
@@ -132,6 +132,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
return SkinUtils.As<TValue>(new BindableFloat(LEGACY_CIRCLE_RADIUS));
|
||||
|
||||
break;
|
||||
|
||||
case OsuSkinConfiguration.HitCircleOverlayAboveNumber:
|
||||
// See https://osu.ppy.sh/help/wiki/Skinning/skin.ini#%5Bgeneral%5D
|
||||
// HitCircleOverlayAboveNumer (with typo) should still be supported for now.
|
||||
return source.GetConfig<OsuSkinConfiguration, TValue>(OsuSkinConfiguration.HitCircleOverlayAboveNumber) ??
|
||||
source.GetConfig<OsuSkinConfiguration, TValue>(OsuSkinConfiguration.HitCircleOverlayAboveNumer);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
AllowSliderBallTint,
|
||||
CursorExpand,
|
||||
CursorRotate,
|
||||
HitCircleOverlayAboveNumber
|
||||
HitCircleOverlayAboveNumber,
|
||||
HitCircleOverlayAboveNumer // Some old skins will have this typo
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Taiko.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
public abstract class TaikoSkinnableTestScene : SkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(TaikoRuleset),
|
||||
typeof(TaikoLegacySkinTransformer),
|
||||
};
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new TaikoRuleset();
|
||||
}
|
||||
}
|
||||
@@ -3,32 +3,31 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osuTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Taiko.Audio;
|
||||
using osu.Game.Rulesets.Taiko.Skinning;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneInputDrum : OsuTestScene
|
||||
public class TestSceneInputDrum : TaikoSkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
||||
{
|
||||
typeof(InputDrum),
|
||||
typeof(DrumSampleMapping),
|
||||
typeof(HitSampleInfo),
|
||||
typeof(SampleControlPoint)
|
||||
};
|
||||
typeof(LegacyInputDrum),
|
||||
}).ToList();
|
||||
|
||||
public TestSceneInputDrum()
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(new TaikoInputManager(new RulesetInfo { ID = 1 })
|
||||
SetContents(() => new TaikoInputManager(new RulesetInfo { ID = 1 })
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new Container
|
||||
|
||||
@@ -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 System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Taiko.Audio;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Skinning
|
||||
{
|
||||
/// <summary>
|
||||
/// A component of the playfield that captures input and displays input as a drum.
|
||||
/// </summary>
|
||||
internal class LegacyInputDrum : Container
|
||||
{
|
||||
public LegacyInputDrum()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
{
|
||||
Texture = skin.GetTexture("taiko-bar-left")
|
||||
},
|
||||
new LegacyHalfDrum(false)
|
||||
{
|
||||
Name = "Left Half",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
RimAction = TaikoAction.LeftRim,
|
||||
CentreAction = TaikoAction.LeftCentre
|
||||
},
|
||||
new LegacyHalfDrum(true)
|
||||
{
|
||||
Name = "Right Half",
|
||||
Anchor = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Scale = new Vector2(-1, 1),
|
||||
RimAction = TaikoAction.RightRim,
|
||||
CentreAction = TaikoAction.RightCentre
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A half-drum. Contains one centre and one rim hit.
|
||||
/// </summary>
|
||||
private class LegacyHalfDrum : Container, IKeyBindingHandler<TaikoAction>
|
||||
{
|
||||
/// <summary>
|
||||
/// The key to be used for the rim of the half-drum.
|
||||
/// </summary>
|
||||
public TaikoAction RimAction;
|
||||
|
||||
/// <summary>
|
||||
/// The key to be used for the centre of the half-drum.
|
||||
/// </summary>
|
||||
public TaikoAction CentreAction;
|
||||
|
||||
private readonly Sprite rimHit;
|
||||
private readonly Sprite centreHit;
|
||||
|
||||
[Resolved]
|
||||
private DrumSampleMapping sampleMappings { get; set; }
|
||||
|
||||
public LegacyHalfDrum(bool flipped)
|
||||
{
|
||||
Masking = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
rimHit = new Sprite
|
||||
{
|
||||
Anchor = flipped ? Anchor.CentreRight : Anchor.CentreLeft,
|
||||
Origin = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
|
||||
Scale = new Vector2(-1, 1),
|
||||
Alpha = 0,
|
||||
},
|
||||
centreHit = new Sprite
|
||||
{
|
||||
Anchor = flipped ? Anchor.CentreRight : Anchor.CentreLeft,
|
||||
Origin = flipped ? Anchor.CentreRight : Anchor.CentreLeft,
|
||||
Alpha = 0,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
rimHit.Texture = skin.GetTexture(@"taiko-drum-outer");
|
||||
centreHit.Texture = skin.GetTexture(@"taiko-drum-inner");
|
||||
}
|
||||
|
||||
public bool OnPressed(TaikoAction action)
|
||||
{
|
||||
Drawable target = null;
|
||||
var drumSample = sampleMappings.SampleAt(Time.Current);
|
||||
|
||||
if (action == CentreAction)
|
||||
{
|
||||
target = centreHit;
|
||||
drumSample.Centre?.Play();
|
||||
}
|
||||
else if (action == RimAction)
|
||||
{
|
||||
target = rimHit;
|
||||
drumSample.Rim?.Play();
|
||||
}
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
const float alpha_amount = 1;
|
||||
|
||||
const float down_time = 80;
|
||||
const float up_time = 50;
|
||||
|
||||
target.Animate(
|
||||
t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time)
|
||||
).Then(
|
||||
t => t.FadeOut(up_time)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(TaikoAction action)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,22 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => source.GetDrawableComponent(component);
|
||||
public Drawable GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
if (!(component is TaikoSkinComponent taikoComponent))
|
||||
return null;
|
||||
|
||||
switch (taikoComponent.Component)
|
||||
{
|
||||
case TaikoSkinComponents.InputDrum:
|
||||
if (GetTexture("taiko-bar-left") != null)
|
||||
return new LegacyInputDrum();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return source.GetDrawableComponent(component);
|
||||
}
|
||||
|
||||
public Texture GetTexture(string componentName) => source.GetTexture(componentName);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko
|
||||
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap, this);
|
||||
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new TaikoLegacySkinTransformer(source);
|
||||
public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new TaikoLegacySkinTransformer(source);
|
||||
|
||||
public const string SHORT_NAME = "taiko";
|
||||
|
||||
|
||||
@@ -5,5 +5,6 @@ namespace osu.Game.Rulesets.Taiko
|
||||
{
|
||||
public enum TaikoSkinComponents
|
||||
{
|
||||
InputDrum,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Audio;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
@@ -22,11 +23,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
private const float middle_split = 0.025f;
|
||||
|
||||
private readonly ControlPointInfo controlPoints;
|
||||
[Cached]
|
||||
private DrumSampleMapping sampleMapping;
|
||||
|
||||
public InputDrum(ControlPointInfo controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
sampleMapping = new DrumSampleMapping(controlPoints);
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
FillMode = FillMode.Fit;
|
||||
@@ -35,35 +37,37 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var sampleMappings = new DrumSampleMapping(controlPoints);
|
||||
|
||||
Children = new Drawable[]
|
||||
Child = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new Container
|
||||
{
|
||||
new TaikoHalfDrum(false, sampleMappings)
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Name = "Left Half",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = -middle_split / 2,
|
||||
RimAction = TaikoAction.LeftRim,
|
||||
CentreAction = TaikoAction.LeftCentre
|
||||
},
|
||||
new TaikoHalfDrum(true, sampleMappings)
|
||||
{
|
||||
Name = "Right Half",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = middle_split / 2,
|
||||
RimAction = TaikoAction.RightRim,
|
||||
CentreAction = TaikoAction.RightCentre
|
||||
new TaikoHalfDrum(false)
|
||||
{
|
||||
Name = "Left Half",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = -middle_split / 2,
|
||||
RimAction = TaikoAction.LeftRim,
|
||||
CentreAction = TaikoAction.LeftCentre
|
||||
},
|
||||
new TaikoHalfDrum(true)
|
||||
{
|
||||
Name = "Right Half",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = middle_split / 2,
|
||||
RimAction = TaikoAction.RightRim,
|
||||
CentreAction = TaikoAction.RightCentre
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddRangeInternal(sampleMappings.Sounds);
|
||||
AddRangeInternal(sampleMapping.Sounds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -86,12 +90,11 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
private readonly Sprite centre;
|
||||
private readonly Sprite centreHit;
|
||||
|
||||
private readonly DrumSampleMapping sampleMappings;
|
||||
[Resolved]
|
||||
private DrumSampleMapping sampleMappings { get; set; }
|
||||
|
||||
public TaikoHalfDrum(bool flipped, DrumSampleMapping sampleMappings)
|
||||
public TaikoHalfDrum(bool flipped)
|
||||
{
|
||||
this.sampleMappings = sampleMappings;
|
||||
|
||||
Masking = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
|
||||
@@ -127,6 +127,31 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
.Assert();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetJsonDecoder()
|
||||
{
|
||||
Decoder<Beatmap> decoder;
|
||||
|
||||
using (var stream = TestResources.OpenResource(normal))
|
||||
using (var sr = new LineBufferedReader(stream))
|
||||
{
|
||||
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
|
||||
|
||||
using (var memStream = new MemoryStream())
|
||||
using (var memWriter = new StreamWriter(memStream))
|
||||
using (var memReader = new LineBufferedReader(memStream))
|
||||
{
|
||||
memWriter.Write(legacyDecoded.Serialize());
|
||||
memWriter.Flush();
|
||||
|
||||
memStream.Position = 0;
|
||||
decoder = Decoder.GetDecoder<Beatmap>(memReader);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsInstanceOf(typeof(JsonBeatmapDecoder), decoder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a .osu file first with a <see cref="LegacyBeatmapDecoder"/>, serializes the resulting <see cref="Beatmap"/> to JSON
|
||||
/// and then deserializes the result back into a <see cref="Beatmap"/> through an <see cref="JsonBeatmapDecoder"/>.
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace osu.Game.Tests.Skins
|
||||
var decoder = new LegacySkinDecoder();
|
||||
using (var resStream = TestResources.OpenResource("skin-empty.ini"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
Assert.IsNull(decoder.Decode(stream).LegacyVersion);
|
||||
Assert.That(decoder.Decode(stream).LegacyVersion, Is.EqualTo(1.0m));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
@@ -12,7 +13,10 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@@ -22,15 +26,15 @@ namespace osu.Game.Tests.Skins
|
||||
[HeadlessTest]
|
||||
public class TestSceneSkinConfigurationLookup : OsuTestScene
|
||||
{
|
||||
private SkinSource source1;
|
||||
private SkinSource source2;
|
||||
private UserSkinSource userSource;
|
||||
private BeatmapSkinSource beatmapSource;
|
||||
private SkinRequester requester;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
Add(new SkinProvidingContainer(source1 = new SkinSource())
|
||||
.WithChild(new SkinProvidingContainer(source2 = new SkinSource())
|
||||
Add(new SkinProvidingContainer(userSource = new UserSkinSource())
|
||||
.WithChild(new SkinProvidingContainer(beatmapSource = new BeatmapSkinSource())
|
||||
.WithChild(requester = new SkinRequester())));
|
||||
});
|
||||
|
||||
@@ -39,31 +43,31 @@ namespace osu.Game.Tests.Skins
|
||||
{
|
||||
AddStep("Add config values", () =>
|
||||
{
|
||||
source1.Configuration.ConfigDictionary["Lookup"] = "source1";
|
||||
source2.Configuration.ConfigDictionary["Lookup"] = "source2";
|
||||
userSource.Configuration.ConfigDictionary["Lookup"] = "user skin";
|
||||
beatmapSource.Configuration.ConfigDictionary["Lookup"] = "beatmap skin";
|
||||
});
|
||||
|
||||
AddAssert("Check lookup finds source2", () => requester.GetConfig<string, string>("Lookup")?.Value == "source2");
|
||||
AddAssert("Check lookup finds beatmap skin", () => requester.GetConfig<string, string>("Lookup")?.Value == "beatmap skin");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFloatLookup()
|
||||
{
|
||||
AddStep("Add config values", () => source1.Configuration.ConfigDictionary["FloatTest"] = "1.1");
|
||||
AddStep("Add config values", () => userSource.Configuration.ConfigDictionary["FloatTest"] = "1.1");
|
||||
AddAssert("Check float parse lookup", () => requester.GetConfig<string, float>("FloatTest")?.Value == 1.1f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBoolLookup()
|
||||
{
|
||||
AddStep("Add config values", () => source1.Configuration.ConfigDictionary["BoolTest"] = "1");
|
||||
AddStep("Add config values", () => userSource.Configuration.ConfigDictionary["BoolTest"] = "1");
|
||||
AddAssert("Check bool parse lookup", () => requester.GetConfig<string, bool>("BoolTest")?.Value == true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnumLookup()
|
||||
{
|
||||
AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Test"] = "Test2");
|
||||
AddStep("Add config values", () => userSource.Configuration.ConfigDictionary["Test"] = "Test2");
|
||||
AddAssert("Check enum parse lookup", () => requester.GetConfig<LookupType, ValueType>(LookupType.Test)?.Value == ValueType.Test2);
|
||||
}
|
||||
|
||||
@@ -76,7 +80,7 @@ namespace osu.Game.Tests.Skins
|
||||
[Test]
|
||||
public void TestLookupNull()
|
||||
{
|
||||
AddStep("Add config values", () => source1.Configuration.ConfigDictionary["Lookup"] = null);
|
||||
AddStep("Add config values", () => userSource.Configuration.ConfigDictionary["Lookup"] = null);
|
||||
|
||||
AddAssert("Check lookup null", () =>
|
||||
{
|
||||
@@ -88,7 +92,7 @@ namespace osu.Game.Tests.Skins
|
||||
[Test]
|
||||
public void TestColourLookup()
|
||||
{
|
||||
AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red);
|
||||
AddStep("Add config colour", () => userSource.Configuration.CustomColours["Lookup"] = Color4.Red);
|
||||
AddAssert("Check colour lookup", () => requester.GetConfig<SkinCustomColourLookup, Color4>(new SkinCustomColourLookup("Lookup"))?.Value == Color4.Red);
|
||||
}
|
||||
|
||||
@@ -101,7 +105,7 @@ namespace osu.Game.Tests.Skins
|
||||
[Test]
|
||||
public void TestWrongColourType()
|
||||
{
|
||||
AddStep("Add config colour", () => source1.Configuration.CustomColours["Lookup"] = Color4.Red);
|
||||
AddStep("Add config colour", () => userSource.Configuration.CustomColours["Lookup"] = Color4.Red);
|
||||
|
||||
AddAssert("perform incorrect lookup", () =>
|
||||
{
|
||||
@@ -127,26 +131,51 @@ namespace osu.Game.Tests.Skins
|
||||
[Test]
|
||||
public void TestEmptyComboColoursNoFallback()
|
||||
{
|
||||
AddStep("Add custom combo colours to source1", () => source1.Configuration.AddComboColours(
|
||||
AddStep("Add custom combo colours to user skin", () => userSource.Configuration.AddComboColours(
|
||||
new Color4(100, 150, 200, 255),
|
||||
new Color4(55, 110, 166, 255),
|
||||
new Color4(75, 125, 175, 255)
|
||||
));
|
||||
|
||||
AddStep("Disallow default colours fallback in source2", () => source2.Configuration.AllowDefaultComboColoursFallback = false);
|
||||
AddStep("Disallow default colours fallback in beatmap skin", () => beatmapSource.Configuration.AllowDefaultComboColoursFallback = false);
|
||||
|
||||
AddAssert("Check retrieved combo colours from source1", () =>
|
||||
requester.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value?.SequenceEqual(source1.Configuration.ComboColours) ?? false);
|
||||
AddAssert("Check retrieved combo colours from user skin", () =>
|
||||
requester.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value?.SequenceEqual(userSource.Configuration.ComboColours) ?? false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLegacyVersionLookup()
|
||||
public void TestNullBeatmapVersionFallsBackToUserSkin()
|
||||
{
|
||||
AddStep("Set source1 version 2.3", () => source1.Configuration.LegacyVersion = 2.3m);
|
||||
AddStep("Set source2 version null", () => source2.Configuration.LegacyVersion = null);
|
||||
AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = 2.3m);
|
||||
AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null);
|
||||
AddAssert("Check legacy version lookup", () => requester.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value == 2.3m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSetBeatmapVersionNoFallback()
|
||||
{
|
||||
AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = 2.3m);
|
||||
AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = 1.7m);
|
||||
AddAssert("Check legacy version lookup", () => requester.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value == 1.7m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNullBeatmapAndUserVersionFallsBackToLatest()
|
||||
{
|
||||
AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = null);
|
||||
AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null);
|
||||
AddAssert("Check legacy version lookup",
|
||||
() => requester.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value == LegacySkinConfiguration.LATEST_VERSION);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIniWithNoVersionFallsBackTo1()
|
||||
{
|
||||
AddStep("Parse skin with no version", () => userSource.Configuration = new LegacySkinDecoder().Decode(new LineBufferedReader(new MemoryStream())));
|
||||
AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null);
|
||||
AddAssert("Check legacy version lookup", () => requester.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value == 1.0m);
|
||||
}
|
||||
|
||||
public enum LookupType
|
||||
{
|
||||
Test
|
||||
@@ -159,14 +188,22 @@ namespace osu.Game.Tests.Skins
|
||||
Test3
|
||||
}
|
||||
|
||||
public class SkinSource : LegacySkin
|
||||
public class UserSkinSource : LegacySkin
|
||||
{
|
||||
public SkinSource()
|
||||
public UserSkinSource()
|
||||
: base(new SkinInfo(), null, null, string.Empty)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class BeatmapSkinSource : LegacyBeatmapSkin
|
||||
{
|
||||
public BeatmapSkinSource()
|
||||
: base(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, null, null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class SkinRequester : Drawable, ISkin
|
||||
{
|
||||
private ISkinSource skin;
|
||||
|
||||
@@ -26,16 +26,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private readonly IReadOnlyList<BreakPeriod> testBreaks = new List<BreakPeriod>
|
||||
{
|
||||
new BreakPeriod
|
||||
{
|
||||
StartTime = 1000,
|
||||
EndTime = 5000,
|
||||
},
|
||||
new BreakPeriod
|
||||
{
|
||||
StartTime = 6000,
|
||||
EndTime = 13500,
|
||||
},
|
||||
new BreakPeriod(1000, 5000),
|
||||
new BreakPeriod(6000, 13500),
|
||||
};
|
||||
|
||||
public TestSceneBreakTracker()
|
||||
@@ -70,7 +62,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestNoEffectsBreak()
|
||||
{
|
||||
var shortBreak = new BreakPeriod { EndTime = 500 };
|
||||
var shortBreak = new BreakPeriod(0, 500);
|
||||
|
||||
setClock(true);
|
||||
loadBreaksStep("short break", new[] { shortBreak });
|
||||
@@ -127,13 +119,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void addShowBreakStep(double seconds)
|
||||
{
|
||||
AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = breakTracker.Breaks = new List<BreakPeriod>
|
||||
AddStep($"show '{seconds}s' break", () =>
|
||||
{
|
||||
new BreakPeriod
|
||||
breakOverlay.Breaks = breakTracker.Breaks = new List<BreakPeriod>
|
||||
{
|
||||
StartTime = Clock.CurrentTime,
|
||||
EndTime = Clock.CurrentTime + seconds * 1000,
|
||||
}
|
||||
new BreakPeriod(Clock.CurrentTime, Clock.CurrentTime + seconds * 1000)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
typeof(StartupScreen),
|
||||
typeof(IntroScreen),
|
||||
typeof(OsuScreen),
|
||||
typeof(IntroTestScene),
|
||||
};
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Position = new Vector2(0, 25),
|
||||
Position = new Vector2(-5, 25),
|
||||
Current = { BindTarget = modSelect.SelectedMods }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
private readonly string filename;
|
||||
private readonly bool drawFallbackGradient;
|
||||
private VideoSprite video;
|
||||
private Video video;
|
||||
|
||||
private ManualClock manualClock;
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace osu.Game.Tournament.Components
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
InternalChild = video = new VideoSprite(stream, false)
|
||||
InternalChild = video = new Video(stream, false)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit,
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (line == null)
|
||||
throw new IOException("Unknown file format (null)");
|
||||
|
||||
var decoder = typedDecoders.Select(d => line.StartsWith(d.Key, StringComparison.InvariantCulture) ? d.Value : null).FirstOrDefault();
|
||||
var decoder = typedDecoders.Where(d => line.StartsWith(d.Key, StringComparison.InvariantCulture)).Select(d => d.Value).FirstOrDefault();
|
||||
|
||||
// it's important the magic does NOT get consumed here, since sometimes it's part of the structure
|
||||
// (see JsonBeatmapDecoder - the magic string is the opening brace)
|
||||
|
||||
@@ -305,12 +305,9 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
case LegacyEventType.Break:
|
||||
double start = getOffsetTime(Parsing.ParseDouble(split[1]));
|
||||
double end = Math.Max(start, getOffsetTime(Parsing.ParseDouble(split[2])));
|
||||
|
||||
var breakEvent = new BreakPeriod
|
||||
{
|
||||
StartTime = start,
|
||||
EndTime = Math.Max(start, getOffsetTime(Parsing.ParseDouble(split[2])))
|
||||
};
|
||||
var breakEvent = new BreakPeriod(start, end);
|
||||
|
||||
if (!breakEvent.HasEffect)
|
||||
return;
|
||||
|
||||
@@ -32,6 +32,17 @@ namespace osu.Game.Beatmaps.Timing
|
||||
/// </summary>
|
||||
public bool HasEffect => Duration >= MIN_BREAK_DURATION;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new break period.
|
||||
/// </summary>
|
||||
/// <param name="startTime">The start time of the break period.</param>
|
||||
/// <param name="endTime">The end time of the break period.</param>
|
||||
public BreakPeriod(double startTime, double endTime)
|
||||
{
|
||||
StartTime = startTime;
|
||||
EndTime = endTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this break contains a specified time.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Threading;
|
||||
|
||||
namespace osu.Game.Extensions
|
||||
{
|
||||
public static class DrawableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper method that is used while <see cref="IKeyBindingHandler"/> doesn't support repetitions of <see cref="IKeyBindingHandler{T}.OnPressed"/>.
|
||||
/// Simulates repetitions by continually invoking a delegate according to the default key repeat rate.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The returned delegate can be cancelled to stop repeat events from firing (usually in <see cref="IKeyBindingHandler{T}.OnReleased"/>).
|
||||
/// </remarks>
|
||||
/// <param name="handler">The <see cref="IKeyBindingHandler{T}"/> which is handling the repeat.</param>
|
||||
/// <param name="scheduler">The <see cref="Scheduler"/> to schedule repetitions on.</param>
|
||||
/// <param name="action">The <see cref="Action"/> to be invoked once immediately and with every repetition.</param>
|
||||
/// <returns>A <see cref="ScheduledDelegate"/> which can be cancelled to stop the repeat events from firing.</returns>
|
||||
public static ScheduledDelegate BeginKeyRepeat(this IKeyBindingHandler handler, Scheduler scheduler, Action action)
|
||||
{
|
||||
action();
|
||||
|
||||
ScheduledDelegate repeatDelegate = new ScheduledDelegate(action, handler.Time.Current + 250, 70);
|
||||
scheduler.Add(repeatDelegate);
|
||||
return repeatDelegate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ namespace osu.Game.Rulesets
|
||||
|
||||
public ModAutoplay GetAutoplayMod() => GetAllMods().OfType<ModAutoplay>().First();
|
||||
|
||||
public virtual ISkin CreateLegacySkinProvider(ISkinSource source) => null;
|
||||
public virtual ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => null;
|
||||
|
||||
protected Ruleset()
|
||||
{
|
||||
|
||||
@@ -9,9 +9,11 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@@ -74,11 +76,9 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
protected virtual bool RelativeScaleBeatLengths => false;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the default <see cref="MultiplierControlPoint"/>s that adjust the scrolling rate of <see cref="HitObject"/>s
|
||||
/// inside this <see cref="DrawableRuleset{TObject}"/>.
|
||||
/// The <see cref="MultiplierControlPoint"/>s that adjust the scrolling rate of <see cref="HitObject"/>s inside this <see cref="DrawableRuleset{TObject}"/>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private readonly SortedList<MultiplierControlPoint> controlPoints = new SortedList<MultiplierControlPoint>(Comparer<MultiplierControlPoint>.Default);
|
||||
protected readonly SortedList<MultiplierControlPoint> ControlPoints = new SortedList<MultiplierControlPoint>(Comparer<MultiplierControlPoint>.Default);
|
||||
|
||||
protected IScrollingInfo ScrollingInfo => scrollingInfo;
|
||||
|
||||
@@ -95,11 +95,11 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
switch (VisualisationMethod)
|
||||
{
|
||||
case ScrollVisualisationMethod.Sequential:
|
||||
scrollingInfo.Algorithm = new SequentialScrollAlgorithm(controlPoints);
|
||||
scrollingInfo.Algorithm = new SequentialScrollAlgorithm(ControlPoints);
|
||||
break;
|
||||
|
||||
case ScrollVisualisationMethod.Overlapping:
|
||||
scrollingInfo.Algorithm = new OverlappingScrollAlgorithm(controlPoints);
|
||||
scrollingInfo.Algorithm = new OverlappingScrollAlgorithm(ControlPoints);
|
||||
break;
|
||||
|
||||
case ScrollVisualisationMethod.Constant:
|
||||
@@ -168,29 +168,10 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
// Collapse sections with the same start time
|
||||
.GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime);
|
||||
|
||||
controlPoints.AddRange(timingChanges);
|
||||
ControlPoints.AddRange(timingChanges);
|
||||
|
||||
if (controlPoints.Count == 0)
|
||||
controlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier });
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (!UserScrollSpeedAdjustment)
|
||||
return false;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.IncreaseScrollSpeed:
|
||||
this.TransformBindableTo(TimeRange, TimeRange.Value - time_span_step, 200, Easing.OutQuint);
|
||||
return true;
|
||||
|
||||
case GlobalAction.DecreaseScrollSpeed:
|
||||
this.TransformBindableTo(TimeRange, TimeRange.Value + time_span_step, 200, Easing.OutQuint);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (ControlPoints.Count == 0)
|
||||
ControlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier });
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -201,8 +182,43 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
throw new ArgumentException($"{nameof(Playfield)} must be a {nameof(ScrollingPlayfield)} when using {nameof(DrawableScrollingRuleset<TObject>)}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the scroll speed of <see cref="HitObject"/>s.
|
||||
/// </summary>
|
||||
/// <param name="amount">The amount to adjust by. Greater than 0 if the scroll speed should be increased, less than 0 if it should be decreased.</param>
|
||||
protected virtual void AdjustScrollSpeed(int amount) => this.TransformBindableTo(TimeRange, TimeRange.Value - amount * time_span_step, 200, Easing.OutQuint);
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (!UserScrollSpeedAdjustment)
|
||||
return false;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.IncreaseScrollSpeed:
|
||||
scheduleScrollSpeedAdjustment(1);
|
||||
return true;
|
||||
|
||||
case GlobalAction.DecreaseScrollSpeed:
|
||||
scheduleScrollSpeedAdjustment(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private ScheduledDelegate scheduledScrollSpeedAdjustment;
|
||||
|
||||
public void OnReleased(GlobalAction action)
|
||||
{
|
||||
scheduledScrollSpeedAdjustment?.Cancel();
|
||||
scheduledScrollSpeedAdjustment = null;
|
||||
}
|
||||
|
||||
private void scheduleScrollSpeedAdjustment(int amount)
|
||||
{
|
||||
scheduledScrollSpeedAdjustment?.Cancel();
|
||||
scheduledScrollSpeedAdjustment = this.BeginKeyRepeat(Scheduler, () => AdjustScrollSpeed(amount));
|
||||
}
|
||||
|
||||
private class LocalScrollingInfo : IScrollingInfo
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace osu.Game.Screens.Edit.Compose
|
||||
|
||||
// the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation
|
||||
// full access to all skin sources.
|
||||
var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider));
|
||||
var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider, EditorBeatmap.PlayableBeatmap));
|
||||
|
||||
// load the skinning hierarchy first.
|
||||
// this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
|
||||
|
||||
@@ -270,10 +270,9 @@ namespace osu.Game.Screens.Menu
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = new VideoSprite(videoStream, false)
|
||||
InternalChild = new Video(videoStream, false)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = new FramedOffsetClock(Clock) { Offset = -logo_1 }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace osu.Game.Screens.Multi
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Spacing = new Vector2(15, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
authorText = new LinkFlowContainer { AutoSizeAxes = Axes.Both },
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
}
|
||||
}
|
||||
|
||||
protected readonly FillFlowContainer<ModIcon> IconsContainer;
|
||||
private readonly FillFlowContainer<ModIcon> iconsContainer;
|
||||
private readonly OsuSpriteText unrankedText;
|
||||
|
||||
public ModDisplay()
|
||||
@@ -50,13 +50,12 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
IconsContainer = new ReverseChildIDFillFlowContainer<ModIcon>
|
||||
iconsContainer = new ReverseChildIDFillFlowContainer<ModIcon>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Margin = new MarginPadding { Left = 10, Right = 10 },
|
||||
},
|
||||
unrankedText = new OsuSpriteText
|
||||
{
|
||||
@@ -69,11 +68,11 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
Current.ValueChanged += mods =>
|
||||
{
|
||||
IconsContainer.Clear();
|
||||
iconsContainer.Clear();
|
||||
|
||||
foreach (Mod mod in mods.NewValue)
|
||||
{
|
||||
IconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) });
|
||||
iconsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.6f) });
|
||||
}
|
||||
|
||||
if (IsLoaded)
|
||||
@@ -92,7 +91,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
base.LoadComplete();
|
||||
|
||||
appearTransform();
|
||||
IconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
|
||||
iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void appearTransform()
|
||||
@@ -104,20 +103,20 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
expand();
|
||||
|
||||
using (IconsContainer.BeginDelayedSequence(1200))
|
||||
using (iconsContainer.BeginDelayedSequence(1200))
|
||||
contract();
|
||||
}
|
||||
|
||||
private void expand()
|
||||
{
|
||||
if (ExpansionMode != ExpansionMode.AlwaysContracted)
|
||||
IconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint);
|
||||
iconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void contract()
|
||||
{
|
||||
if (ExpansionMode != ExpansionMode.AlwaysExpanded)
|
||||
IconsContainer.TransformSpacingTo(new Vector2(-25, 0), 500, Easing.OutQuint);
|
||||
iconsContainer.TransformSpacingTo(new Vector2(-25, 0), 500, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
|
||||
@@ -285,7 +285,7 @@ namespace osu.Game.Screens.Play
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Top = 20, Right = 10 },
|
||||
Margin = new MarginPadding { Top = 20, Right = 20 },
|
||||
};
|
||||
|
||||
protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset?.FirstAvailableHitWindows);
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace osu.Game.Screens.Play
|
||||
dependencies.CacheAs(gameplayBeatmap);
|
||||
|
||||
addUnderlayComponents(GameplayClockContainer);
|
||||
addGameplayComponents(GameplayClockContainer, Beatmap.Value);
|
||||
addGameplayComponents(GameplayClockContainer, Beatmap.Value, playableBeatmap);
|
||||
addOverlayComponents(GameplayClockContainer, Beatmap.Value);
|
||||
|
||||
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
||||
@@ -214,13 +214,13 @@ namespace osu.Game.Screens.Play
|
||||
target.Add(DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both });
|
||||
}
|
||||
|
||||
private void addGameplayComponents(Container target, WorkingBeatmap working)
|
||||
private void addGameplayComponents(Container target, WorkingBeatmap working, IBeatmap playableBeatmap)
|
||||
{
|
||||
var beatmapSkinProvider = new BeatmapSkinProvidingContainer(working.Skin);
|
||||
|
||||
// the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation
|
||||
// full access to all skin sources.
|
||||
var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider));
|
||||
var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider, playableBeatmap));
|
||||
|
||||
// load the skinning hierarchy first.
|
||||
// this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
|
||||
|
||||
@@ -30,6 +30,9 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
private readonly ScoreInfo score;
|
||||
|
||||
private readonly List<StatisticDisplay> statisticDisplays = new List<StatisticDisplay>();
|
||||
|
||||
private FillFlowContainer starAndModDisplay;
|
||||
|
||||
private RollingCounter<long> scoreCounter;
|
||||
|
||||
/// <summary>
|
||||
@@ -119,11 +122,12 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
},
|
||||
new FillFlowContainer
|
||||
starAndModDisplay = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(5, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new StarRatingDisplay(beatmap)
|
||||
@@ -131,15 +135,6 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft
|
||||
},
|
||||
new ModDisplay
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
DisplayUnrankedText = false,
|
||||
ExpansionMode = ExpansionMode.AlwaysExpanded,
|
||||
Scale = new Vector2(0.5f),
|
||||
Current = { Value = score.Mods }
|
||||
}
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
@@ -214,6 +209,19 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (score.Mods.Any())
|
||||
{
|
||||
starAndModDisplay.Add(new ModDisplay
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
DisplayUnrankedText = false,
|
||||
ExpansionMode = ExpansionMode.AlwaysExpanded,
|
||||
Scale = new Vector2(0.5f),
|
||||
Current = { Value = score.Mods }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
||||
@@ -27,18 +27,19 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
protected readonly OsuSpriteText MultiplierText;
|
||||
private readonly FooterModDisplay modDisplay;
|
||||
private readonly ModDisplay modDisplay;
|
||||
private Color4 lowMultiplierColour;
|
||||
private Color4 highMultiplierColour;
|
||||
|
||||
public FooterButtonMods()
|
||||
{
|
||||
ButtonContentContainer.Add(modDisplay = new FooterModDisplay
|
||||
ButtonContentContainer.Add(modDisplay = new ModDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
DisplayUnrankedText = false,
|
||||
Scale = new Vector2(0.8f)
|
||||
Scale = new Vector2(0.8f),
|
||||
ExpansionMode = ExpansionMode.AlwaysContracted,
|
||||
});
|
||||
ButtonContentContainer.Add(MultiplierText = new OsuSpriteText
|
||||
{
|
||||
@@ -84,16 +85,5 @@ namespace osu.Game.Screens.Select
|
||||
else
|
||||
modDisplay.FadeOut();
|
||||
}
|
||||
|
||||
private class FooterModDisplay : ModDisplay
|
||||
{
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent?.Parent?.ReceivePositionalInputAt(screenSpacePos) ?? false;
|
||||
|
||||
public FooterModDisplay()
|
||||
{
|
||||
ExpansionMode = ExpansionMode.AlwaysContracted;
|
||||
IconsContainer.Margin = new MarginPadding();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
@@ -18,6 +19,20 @@ namespace osu.Game.Skinning
|
||||
Configuration.AllowDefaultComboColoursFallback = false;
|
||||
}
|
||||
|
||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
switch (lookup)
|
||||
{
|
||||
case LegacySkinConfiguration.LegacySetting s when s == LegacySkinConfiguration.LegacySetting.Version:
|
||||
if (Configuration.LegacyVersion is decimal version)
|
||||
return SkinUtils.As<TValue>(new Bindable<decimal>(version));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return base.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
|
||||
private static SkinInfo createSkinInfo(BeatmapInfo beatmap) =>
|
||||
new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() };
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@@ -24,6 +25,8 @@ namespace osu.Game.Skinning
|
||||
|
||||
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
|
||||
|
||||
public Dictionary<string, string> ImageLookups = new Dictionary<string, string>();
|
||||
|
||||
public readonly float[] ColumnLineWidth;
|
||||
public readonly float[] ColumnSpacing;
|
||||
public readonly float[] ColumnWidth;
|
||||
@@ -45,5 +48,13 @@ namespace osu.Game.Skinning
|
||||
ColumnLineWidth.AsSpan().Fill(2);
|
||||
ColumnWidth.AsSpan().Fill(DEFAULT_COLUMN_SIZE);
|
||||
}
|
||||
|
||||
private float? minimumColumnWidth;
|
||||
|
||||
public float MinimumColumnWidth
|
||||
{
|
||||
get => minimumColumnWidth ?? ColumnWidth.Min();
|
||||
set => minimumColumnWidth = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ namespace osu.Game.Skinning
|
||||
HoldNoteBodyImage,
|
||||
ExplosionImage,
|
||||
ExplosionScale,
|
||||
ColumnLineColour
|
||||
ColumnLineColour,
|
||||
JudgementLineColour,
|
||||
ColumnBackgroundColour,
|
||||
ColumnLightColour,
|
||||
MinimumColumnWidth
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,12 +71,6 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
var pair = SplitKeyVal(line);
|
||||
|
||||
if (pair.Key.StartsWith("Colour"))
|
||||
{
|
||||
HandleColours(currentConfig, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (pair.Key)
|
||||
{
|
||||
case "ColumnLineWidth":
|
||||
@@ -106,8 +100,26 @@ namespace osu.Game.Skinning
|
||||
case "LightingNWidth":
|
||||
parseArrayValue(pair.Value, currentConfig.ExplosionWidth);
|
||||
break;
|
||||
|
||||
case "WidthForNoteHeightScale":
|
||||
currentConfig.MinimumColumnWidth = float.Parse(pair.Value, CultureInfo.InvariantCulture) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
|
||||
break;
|
||||
|
||||
case string _ when pair.Key.StartsWith("Colour"):
|
||||
HandleColours(currentConfig, line);
|
||||
break;
|
||||
|
||||
case string _ when pair.Key.StartsWith("NoteImage"):
|
||||
currentConfig.ImageLookups[pair.Key] = pair.Value;
|
||||
break;
|
||||
|
||||
case string _ when pair.Key.StartsWith("KeyImage"):
|
||||
currentConfig.ImageLookups[pair.Key] = pair.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pendingLines.Clear();
|
||||
}
|
||||
|
||||
private void parseArrayValue(string value, float[] output)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
}
|
||||
else
|
||||
Configuration = new LegacySkinConfiguration { LegacyVersion = LegacySkinConfiguration.LATEST_VERSION };
|
||||
Configuration = new LegacySkinConfiguration();
|
||||
}
|
||||
|
||||
if (storage != null)
|
||||
@@ -88,7 +88,8 @@ namespace osu.Game.Skinning
|
||||
|
||||
// todo: this shouldn't really be duplicated here (from ManiaLegacySkinTransformer). we need to come up with a better solution.
|
||||
hasKeyTexture = new Lazy<bool>(() => this.GetAnimation(
|
||||
lookupForMania<string>(new LegacyManiaSkinConfigurationLookup(4, LegacyManiaSkinConfigurationLookups.KeyImage, 0))?.Value ?? "mania-key1", true, true) != null);
|
||||
lookupForMania<string>(new LegacyManiaSkinConfigurationLookup(4, LegacyManiaSkinConfigurationLookups.KeyImage, 0))?.Value ?? "mania-key1", true,
|
||||
true) != null);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@@ -122,10 +123,7 @@ namespace osu.Game.Skinning
|
||||
switch (legacy)
|
||||
{
|
||||
case LegacySkinConfiguration.LegacySetting.Version:
|
||||
if (Configuration.LegacyVersion is decimal version)
|
||||
return SkinUtils.As<TValue>(new Bindable<decimal>(version));
|
||||
|
||||
break;
|
||||
return SkinUtils.As<TValue>(new Bindable<decimal>(Configuration.LegacyVersion ?? LegacySkinConfiguration.LATEST_VERSION));
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -207,6 +205,44 @@ namespace osu.Game.Skinning
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.ColumnLineColour:
|
||||
return SkinUtils.As<TValue>(getCustomColour(existing, "ColourColumnLine"));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.JudgementLineColour:
|
||||
return SkinUtils.As<TValue>(getCustomColour(existing, "ColourJudgementLine"));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour:
|
||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||
return SkinUtils.As<TValue>(getCustomColour(existing, $"Colour{maniaLookup.TargetColumn + 1}"));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.ColumnLightColour:
|
||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||
return SkinUtils.As<TValue>(getCustomColour(existing, $"ColourLight{maniaLookup.TargetColumn + 1}"));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.MinimumColumnWidth:
|
||||
return SkinUtils.As<TValue>(new Bindable<float>(existing.MinimumColumnWidth));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.NoteImage:
|
||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||
return SkinUtils.As<TValue>(getManiaImage(existing, $"NoteImage{maniaLookup.TargetColumn}"));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage:
|
||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||
return SkinUtils.As<TValue>(getManiaImage(existing, $"NoteImage{maniaLookup.TargetColumn}H"));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.HoldNoteTailImage:
|
||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||
return SkinUtils.As<TValue>(getManiaImage(existing, $"NoteImage{maniaLookup.TargetColumn}T"));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.HoldNoteBodyImage:
|
||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||
return SkinUtils.As<TValue>(getManiaImage(existing, $"NoteImage{maniaLookup.TargetColumn}L"));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.KeyImage:
|
||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||
return SkinUtils.As<TValue>(getManiaImage(existing, $"KeyImage{maniaLookup.TargetColumn}"));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.KeyImageDown:
|
||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||
return SkinUtils.As<TValue>(getManiaImage(existing, $"KeyImage{maniaLookup.TargetColumn}D"));
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -215,6 +251,9 @@ namespace osu.Game.Skinning
|
||||
private IBindable<Color4> getCustomColour(IHasCustomColours source, string lookup)
|
||||
=> source.CustomColours.TryGetValue(lookup, out var col) ? new Bindable<Color4>(col) : null;
|
||||
|
||||
private IBindable<string> getManiaImage(LegacyManiaSkinConfiguration source, string lookup)
|
||||
=> source.ImageLookups.TryGetValue(lookup, out var image) ? new Bindable<string>(image) : null;
|
||||
|
||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
switch (component)
|
||||
@@ -243,21 +282,24 @@ namespace osu.Game.Skinning
|
||||
|
||||
public override Texture GetTexture(string componentName)
|
||||
{
|
||||
componentName = getFallbackName(componentName);
|
||||
|
||||
float ratio = 2;
|
||||
var texture = Textures?.Get($"{componentName}@2x");
|
||||
|
||||
if (texture == null)
|
||||
foreach (var name in getFallbackNames(componentName))
|
||||
{
|
||||
ratio = 1;
|
||||
texture = Textures?.Get(componentName);
|
||||
float ratio = 2;
|
||||
var texture = Textures?.Get($"{name}@2x");
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
ratio = 1;
|
||||
texture = Textures?.Get(name);
|
||||
}
|
||||
|
||||
if (texture != null)
|
||||
texture.ScaleAdjust = ratio;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
if (texture != null)
|
||||
texture.ScaleAdjust = ratio;
|
||||
|
||||
return texture;
|
||||
return null;
|
||||
}
|
||||
|
||||
public override SampleChannel GetSample(ISampleInfo sampleInfo)
|
||||
@@ -277,10 +319,14 @@ namespace osu.Game.Skinning
|
||||
return null;
|
||||
}
|
||||
|
||||
private string getFallbackName(string componentName)
|
||||
private IEnumerable<string> getFallbackNames(string componentName)
|
||||
{
|
||||
// May be something like "Gameplay/osu/approachcircle" from lazer, or "Arrows/note1" from a user skin.
|
||||
yield return componentName;
|
||||
|
||||
// Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle").
|
||||
string lastPiece = componentName.Split('/').Last();
|
||||
return componentName.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece;
|
||||
yield return componentName.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,5 +52,12 @@ namespace osu.Game.Skinning
|
||||
|
||||
base.ParseLine(skin, section, line);
|
||||
}
|
||||
|
||||
protected override LegacySkinConfiguration CreateTemplateObject()
|
||||
{
|
||||
var config = base.CreateTemplateObject();
|
||||
config.LegacyVersion = 1.0m;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,14 +8,13 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Timing;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public static class LegacySkinExtensions
|
||||
{
|
||||
public static Drawable GetAnimation(this ISkin source, string componentName, bool animatable, bool looping, bool applyConfigFrameRate = false, string animationSeparator = "-",
|
||||
bool startAtCurrentTime = false, double? frameLength = null)
|
||||
bool startAtCurrentTime = true, double? frameLength = null)
|
||||
{
|
||||
Texture texture;
|
||||
|
||||
@@ -28,7 +27,7 @@ namespace osu.Game.Skinning
|
||||
var animation = new SkinnableTextureAnimation(startAtCurrentTime)
|
||||
{
|
||||
DefaultFrameLength = frameLength ?? getFrameLength(source, applyConfigFrameRate, textures),
|
||||
Repeat = looping,
|
||||
Loop = looping,
|
||||
};
|
||||
|
||||
foreach (var t in textures)
|
||||
@@ -71,7 +70,10 @@ namespace osu.Game.Skinning
|
||||
base.LoadComplete();
|
||||
|
||||
if (timeReference != null)
|
||||
Clock = new FramedOffsetClock(timeReference.Clock) { Offset = -timeReference.AnimationStartTime };
|
||||
{
|
||||
Clock = timeReference.Clock;
|
||||
PlaybackPosition = timeReference.Clock.CurrentTime - timeReference.AnimationStartTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Database;
|
||||
|
||||
@@ -27,7 +28,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
foreach (var filename in base.GetFilenames(name))
|
||||
{
|
||||
var path = getPathForFile(filename);
|
||||
var path = getPathForFile(filename.ToStandardisedPath());
|
||||
if (path != null)
|
||||
yield return path;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace osu.Game.Storyboards.Drawables
|
||||
Animation = animation;
|
||||
Origin = animation.Origin;
|
||||
Position = animation.InitialPosition;
|
||||
Repeat = animation.LoopType == AnimationLoopType.LoopForever;
|
||||
Loop = animation.LoopType == AnimationLoopType.LoopForever;
|
||||
|
||||
LifetimeStart = animation.StartTime;
|
||||
LifetimeEnd = animation.EndTime;
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Graphics.Video;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Storyboards.Drawables
|
||||
@@ -16,7 +15,7 @@ namespace osu.Game.Storyboards.Drawables
|
||||
public class DrawableStoryboardVideo : CompositeDrawable
|
||||
{
|
||||
public readonly StoryboardVideo Video;
|
||||
private VideoSprite videoSprite;
|
||||
private Video video;
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
@@ -40,14 +39,13 @@ namespace osu.Game.Storyboards.Drawables
|
||||
if (stream == null)
|
||||
return;
|
||||
|
||||
InternalChild = videoSprite = new VideoSprite(stream, false)
|
||||
InternalChild = video = new Video(stream, false)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fill,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Alpha = 0,
|
||||
Clock = new FramedOffsetClock(Clock) { Offset = -Video.StartTime }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,10 +53,12 @@ namespace osu.Game.Storyboards.Drawables
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (videoSprite == null) return;
|
||||
if (video == null) return;
|
||||
|
||||
using (videoSprite.BeginAbsoluteSequence(0))
|
||||
videoSprite.FadeIn(500);
|
||||
video.PlaybackPosition = Clock.CurrentTime - Video.StartTime;
|
||||
|
||||
using (video.BeginAbsoluteSequence(0))
|
||||
video.FadeIn(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@@ -30,10 +32,13 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
}
|
||||
|
||||
// Required to be part of the per-ruleset implementation to construct the newer version of the Ruleset.
|
||||
protected abstract Ruleset CreateRulesetForSkinProvider();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, SkinManager skinManager)
|
||||
{
|
||||
var dllStore = new DllResourceStore(GetType().Assembly);
|
||||
var dllStore = new DllResourceStore(DynamicCompilationOriginal.GetType().Assembly);
|
||||
|
||||
metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
|
||||
defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
|
||||
@@ -47,16 +52,18 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
createdDrawables.Clear();
|
||||
|
||||
Cell(0).Child = createProvider(null, creationFunction);
|
||||
Cell(1).Child = createProvider(metricsSkin, creationFunction);
|
||||
Cell(2).Child = createProvider(defaultSkin, creationFunction);
|
||||
Cell(3).Child = createProvider(specialSkin, creationFunction);
|
||||
Cell(4).Child = createProvider(oldSkin, creationFunction);
|
||||
var beatmap = CreateBeatmapForSkinProvider();
|
||||
|
||||
Cell(0).Child = createProvider(null, creationFunction, beatmap);
|
||||
Cell(1).Child = createProvider(metricsSkin, creationFunction, beatmap);
|
||||
Cell(2).Child = createProvider(defaultSkin, creationFunction, beatmap);
|
||||
Cell(3).Child = createProvider(specialSkin, creationFunction, beatmap);
|
||||
Cell(4).Child = createProvider(oldSkin, creationFunction, beatmap);
|
||||
}
|
||||
|
||||
protected IEnumerable<Drawable> CreatedDrawables => createdDrawables;
|
||||
|
||||
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction)
|
||||
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction, IBeatmap beatmap)
|
||||
{
|
||||
var created = creationFunction();
|
||||
createdDrawables.Add(created);
|
||||
@@ -100,7 +107,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
new OutlineBox { Alpha = autoSize ? 1 : 0 },
|
||||
mainProvider.WithChild(
|
||||
new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider))
|
||||
new SkinProvidingContainer(CreateRulesetForSkinProvider().CreateLegacySkinProvider(mainProvider, beatmap))
|
||||
{
|
||||
Child = created,
|
||||
RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None,
|
||||
@@ -113,6 +120,8 @@ namespace osu.Game.Tests.Visual
|
||||
};
|
||||
}
|
||||
|
||||
protected virtual IBeatmap CreateBeatmapForSkinProvider() => CreateWorkingBeatmap(Ruleset.Value).GetPlayableBeatmap(Ruleset.Value);
|
||||
|
||||
private class OutlineBox : CompositeDrawable
|
||||
{
|
||||
public OutlineBox()
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.402.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.402.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.407.1" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.403.0" />
|
||||
<PackageReference Include="Sentry" Version="2.1.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
+4
-4
@@ -70,8 +70,8 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.402.1" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.402.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.407.1" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.403.0" />
|
||||
</ItemGroup>
|
||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
@@ -79,8 +79,8 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.402.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.407.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
|
||||
Reference in New Issue
Block a user