mirror of
https://github.com/ppy/osu.git
synced 2025-02-05 04:13:03 +08:00
Merge branch 'master' into mania-notelock
This commit is contained in:
commit
aa151e4cd7
@ -6,6 +6,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Skinning
|
namespace osu.Game.Rulesets.Catch.Skinning
|
||||||
{
|
{
|
||||||
@ -61,7 +62,12 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
|||||||
switch (lookup)
|
switch (lookup)
|
||||||
{
|
{
|
||||||
case CatchSkinColour colour:
|
case CatchSkinColour colour:
|
||||||
return Source.GetConfig<SkinCustomColourLookup, TValue>(new SkinCustomColourLookup(colour));
|
var result = (Bindable<Color4>)Source.GetConfig<SkinCustomColourLookup, TValue>(new SkinCustomColourLookup(colour));
|
||||||
|
if (result == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
result.Value = LegacyColourCompatibility.DisallowZeroAlpha(result.Value);
|
||||||
|
return (IBindable<TValue>)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Source.GetConfig<TLookup, TValue>(lookup);
|
return Source.GetConfig<TLookup, TValue>(lookup);
|
||||||
|
@ -40,7 +40,6 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
|||||||
colouredSprite = new Sprite
|
colouredSprite = new Sprite
|
||||||
{
|
{
|
||||||
Texture = skin.GetTexture(lookupName),
|
Texture = skin.GetTexture(lookupName),
|
||||||
Colour = drawableObject.AccentColour.Value,
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
@ -76,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
accentColour.BindValueChanged(colour => colouredSprite.Colour = colour.NewValue, true);
|
accentColour.BindValueChanged(colour => colouredSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
[TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(ManiaModPerfect) })]
|
[TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath, new[] { typeof(ManiaModPerfect) })]
|
||||||
[TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath | LegacyMods.DoubleTime, new[] { typeof(ManiaModDoubleTime), typeof(ManiaModPerfect) })]
|
[TestCase(LegacyMods.Perfect | LegacyMods.SuddenDeath | LegacyMods.DoubleTime, new[] { typeof(ManiaModDoubleTime), typeof(ManiaModPerfect) })]
|
||||||
[TestCase(LegacyMods.Random | LegacyMods.SuddenDeath, new[] { typeof(ManiaModRandom), typeof(ManiaModSuddenDeath) })]
|
[TestCase(LegacyMods.Random | LegacyMods.SuddenDeath, new[] { typeof(ManiaModRandom), typeof(ManiaModSuddenDeath) })]
|
||||||
|
[TestCase(LegacyMods.Flashlight | LegacyMods.Mirror, new[] { typeof(ManiaModFlashlight), typeof(ManiaModMirror) })]
|
||||||
public new void Test(LegacyMods legacyMods, Type[] expectedMods) => base.Test(legacyMods, expectedMods);
|
public new void Test(LegacyMods legacyMods, Type[] expectedMods) => base.Test(legacyMods, expectedMods);
|
||||||
|
|
||||||
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||||
|
@ -22,18 +22,22 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[Cached]
|
[Cached]
|
||||||
private readonly Column column;
|
private readonly Column column;
|
||||||
|
|
||||||
public ColumnTestContainer(int column, ManiaAction action)
|
public ColumnTestContainer(int column, ManiaAction action, bool showColumn = false)
|
||||||
{
|
{
|
||||||
this.column = new Column(column)
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
Action = { Value = action },
|
this.column = new Column(column)
|
||||||
AccentColour = Color4.Orange,
|
{
|
||||||
ColumnType = column % 2 == 0 ? ColumnType.Even : ColumnType.Odd
|
Action = { Value = action },
|
||||||
};
|
AccentColour = Color4.Orange,
|
||||||
|
ColumnType = column % 2 == 0 ? ColumnType.Even : ColumnType.Odd,
|
||||||
InternalChild = content = new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4)
|
Alpha = showColumn ? 1 : 0
|
||||||
{
|
},
|
||||||
RelativeSizeAxes = Axes.Both
|
content = new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
this.column.TopLevelContainer.CreateProxy()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new ColumnTestContainer(0, ManiaAction.Key1)
|
new ColumnTestContainer(0, ManiaAction.Key1, true)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
new ColumnTestContainer(1, ManiaAction.Key2)
|
new ColumnTestContainer(1, ManiaAction.Key2, true)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -126,6 +126,9 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Random))
|
if (mods.HasFlag(LegacyMods.Random))
|
||||||
yield return new ManiaModRandom();
|
yield return new ManiaModRandom();
|
||||||
|
|
||||||
|
if (mods.HasFlag(LegacyMods.Mirror))
|
||||||
|
yield return new ManiaModMirror();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override LegacyMods ConvertToLegacyMods(Mod[] mods)
|
public override LegacyMods ConvertToLegacyMods(Mod[] mods)
|
||||||
@ -175,6 +178,10 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
case ManiaModFadeIn _:
|
case ManiaModFadeIn _:
|
||||||
value |= LegacyMods.FadeIn;
|
value |= LegacyMods.FadeIn;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ManiaModMirror _:
|
||||||
|
value |= LegacyMods.Mirror;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,6 +333,16 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
Height = 250
|
Height = 250
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
new StatisticRow
|
||||||
|
{
|
||||||
|
Columns = new[]
|
||||||
|
{
|
||||||
|
new StatisticItem(string.Empty, new SimpleStatisticTable(3, new SimpleStatisticItem[]
|
||||||
|
{
|
||||||
|
new UnstableRate(score.HitEvents)
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -238,7 +238,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (Tail.AllJudged)
|
if (Tail.AllJudged)
|
||||||
|
{
|
||||||
ApplyResult(r => r.Type = HitResult.Perfect);
|
ApplyResult(r => r.Type = HitResult.Perfect);
|
||||||
|
endHold();
|
||||||
|
}
|
||||||
|
|
||||||
if (Tail.Result.Type == HitResult.Miss)
|
if (Tail.Result.Type == HitResult.Miss)
|
||||||
HasBroken = true;
|
HasBroken = true;
|
||||||
|
46
osu.Game.Rulesets.Mania/Skinning/HitTargetInsetContainer.cs
Normal file
46
osu.Game.Rulesets.Mania/Skinning/HitTargetInsetContainer.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Skinning
|
||||||
|
{
|
||||||
|
public class HitTargetInsetContainer : Container
|
||||||
|
{
|
||||||
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
private readonly Container content;
|
||||||
|
|
||||||
|
private float hitPosition;
|
||||||
|
|
||||||
|
public HitTargetInsetContainer()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = content = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
|
||||||
|
{
|
||||||
|
hitPosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.HitPosition)?.Value ?? Stage.HIT_TARGET_POSITION;
|
||||||
|
|
||||||
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
|
direction.BindValueChanged(onDirectionChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||||
|
{
|
||||||
|
content.Padding = direction.NewValue == ScrollingDirection.Up
|
||||||
|
? new MarginPadding { Top = hitPosition }
|
||||||
|
: new MarginPadding { Bottom = hitPosition };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -19,7 +21,14 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
private readonly IBindable<bool> isHitting = new Bindable<bool>();
|
private readonly IBindable<bool> isHitting = new Bindable<bool>();
|
||||||
|
|
||||||
private Drawable sprite;
|
[CanBeNull]
|
||||||
|
private Drawable bodySprite;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private Drawable lightContainer;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private Drawable light;
|
||||||
|
|
||||||
public LegacyBodyPiece()
|
public LegacyBodyPiece()
|
||||||
{
|
{
|
||||||
@ -32,7 +41,39 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
string imageName = GetColumnSkinConfig<string>(skin, LegacyManiaSkinConfigurationLookups.HoldNoteBodyImage)?.Value
|
string imageName = GetColumnSkinConfig<string>(skin, LegacyManiaSkinConfigurationLookups.HoldNoteBodyImage)?.Value
|
||||||
?? $"mania-note{FallbackColumnIndex}L";
|
?? $"mania-note{FallbackColumnIndex}L";
|
||||||
|
|
||||||
sprite = skin.GetAnimation(imageName, WrapMode.ClampToEdge, WrapMode.ClampToEdge, true, true).With(d =>
|
string lightImage = GetColumnSkinConfig<string>(skin, LegacyManiaSkinConfigurationLookups.HoldNoteLightImage)?.Value
|
||||||
|
?? "lightingL";
|
||||||
|
|
||||||
|
float lightScale = GetColumnSkinConfig<float>(skin, LegacyManiaSkinConfigurationLookups.HoldNoteLightScale)?.Value
|
||||||
|
?? 1;
|
||||||
|
|
||||||
|
// Create a temporary animation to retrieve the number of frames, in an effort to calculate the intended frame length.
|
||||||
|
// This animation is discarded and re-queried with the appropriate frame length afterwards.
|
||||||
|
var tmp = skin.GetAnimation(lightImage, true, false);
|
||||||
|
double frameLength = 0;
|
||||||
|
if (tmp is IFramedAnimation tmpAnimation && tmpAnimation.FrameCount > 0)
|
||||||
|
frameLength = Math.Max(1000 / 60.0, 170.0 / tmpAnimation.FrameCount);
|
||||||
|
|
||||||
|
light = skin.GetAnimation(lightImage, true, true, frameLength: frameLength).With(d =>
|
||||||
|
{
|
||||||
|
if (d == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
d.Origin = Anchor.Centre;
|
||||||
|
d.Blending = BlendingParameters.Additive;
|
||||||
|
d.Scale = new Vector2(lightScale);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (light != null)
|
||||||
|
{
|
||||||
|
lightContainer = new HitTargetInsetContainer
|
||||||
|
{
|
||||||
|
Alpha = 0,
|
||||||
|
Child = light
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bodySprite = skin.GetAnimation(imageName, WrapMode.ClampToEdge, WrapMode.ClampToEdge, true, true).With(d =>
|
||||||
{
|
{
|
||||||
if (d == null)
|
if (d == null)
|
||||||
return;
|
return;
|
||||||
@ -47,8 +88,8 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
// Todo: Wrap
|
// Todo: Wrap
|
||||||
});
|
});
|
||||||
|
|
||||||
if (sprite != null)
|
if (bodySprite != null)
|
||||||
InternalChild = sprite;
|
InternalChild = bodySprite;
|
||||||
|
|
||||||
direction.BindTo(scrollingInfo.Direction);
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
direction.BindValueChanged(onDirectionChanged, true);
|
direction.BindValueChanged(onDirectionChanged, true);
|
||||||
@ -60,28 +101,68 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
|
|
||||||
private void onIsHittingChanged(ValueChangedEvent<bool> isHitting)
|
private void onIsHittingChanged(ValueChangedEvent<bool> isHitting)
|
||||||
{
|
{
|
||||||
if (!(sprite is TextureAnimation animation))
|
if (bodySprite is TextureAnimation bodyAnimation)
|
||||||
|
{
|
||||||
|
bodyAnimation.GotoFrame(0);
|
||||||
|
bodyAnimation.IsPlaying = isHitting.NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lightContainer == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
animation.GotoFrame(0);
|
if (isHitting.NewValue)
|
||||||
animation.IsPlaying = isHitting.NewValue;
|
{
|
||||||
|
// Clear the fade out and, more importantly, the removal.
|
||||||
|
lightContainer.ClearTransforms();
|
||||||
|
|
||||||
|
// Only add the container if the removal has taken place.
|
||||||
|
if (lightContainer.Parent == null)
|
||||||
|
Column.TopLevelContainer.Add(lightContainer);
|
||||||
|
|
||||||
|
// The light must be seeked only after being loaded, otherwise a nullref occurs (https://github.com/ppy/osu-framework/issues/3847).
|
||||||
|
if (light is TextureAnimation lightAnimation)
|
||||||
|
lightAnimation.GotoFrame(0);
|
||||||
|
|
||||||
|
lightContainer.FadeIn(80);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lightContainer.FadeOut(120)
|
||||||
|
.OnComplete(d => Column.TopLevelContainer.Remove(d));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||||
{
|
{
|
||||||
if (sprite == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (direction.NewValue == ScrollingDirection.Up)
|
if (direction.NewValue == ScrollingDirection.Up)
|
||||||
{
|
{
|
||||||
sprite.Origin = Anchor.BottomCentre;
|
if (bodySprite != null)
|
||||||
sprite.Scale = new Vector2(1, -1);
|
{
|
||||||
|
bodySprite.Origin = Anchor.BottomCentre;
|
||||||
|
bodySprite.Scale = new Vector2(1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (light != null)
|
||||||
|
light.Anchor = Anchor.TopCentre;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sprite.Origin = Anchor.TopCentre;
|
if (bodySprite != null)
|
||||||
sprite.Scale = Vector2.One;
|
{
|
||||||
|
bodySprite.Origin = Anchor.TopCentre;
|
||||||
|
bodySprite.Scale = Vector2.One;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (light != null)
|
||||||
|
light.Anchor = Anchor.BottomCentre;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
lightContainer?.Expire();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.BottomCentre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
Colour = lightColour,
|
Colour = LegacyColourCompatibility.DisallowZeroAlpha(lightColour),
|
||||||
Texture = skin.GetTexture(lightImage),
|
Texture = skin.GetTexture(lightImage),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Width = 1,
|
Width = 1,
|
||||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = 1,
|
Height = 1,
|
||||||
Colour = lineColour,
|
Colour = LegacyColourCompatibility.DisallowZeroAlpha(lineColour),
|
||||||
Alpha = showJudgementLine ? 0.9f : 0
|
Alpha = showJudgementLine ? 0.9f : 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,9 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
|
|
||||||
direction.BindTo(scrollingInfo.Direction);
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
direction.BindValueChanged(onDirectionChanged, true);
|
direction.BindValueChanged(onDirectionChanged, true);
|
||||||
|
|
||||||
|
if (GetColumnSkinConfig<bool>(skin, LegacyManiaSkinConfigurationLookups.KeysUnderNotes)?.Value ?? false)
|
||||||
|
Column.UnderlayElements.Add(CreateProxy());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||||
|
@ -2,14 +2,12 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -108,75 +106,43 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
LegacyColourCompatibility.ApplyWithDoubledAlpha(new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both
|
||||||
Child = new Box
|
}, backgroundColour),
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = backgroundColour
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new HitTargetInsetContainer
|
new HitTargetInsetContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new Box
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Width = leftLineWidth,
|
Width = leftLineWidth,
|
||||||
Scale = new Vector2(0.740f, 1),
|
Scale = new Vector2(0.740f, 1),
|
||||||
Colour = lineColour,
|
Alpha = hasLeftLine ? 1 : 0,
|
||||||
Alpha = hasLeftLine ? 1 : 0
|
Child = LegacyColourCompatibility.ApplyWithDoubledAlpha(new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
}, lineColour)
|
||||||
},
|
},
|
||||||
new Box
|
new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Width = rightLineWidth,
|
Width = rightLineWidth,
|
||||||
Scale = new Vector2(0.740f, 1),
|
Scale = new Vector2(0.740f, 1),
|
||||||
Colour = lineColour,
|
Alpha = hasRightLine ? 1 : 0,
|
||||||
Alpha = hasRightLine ? 1 : 0
|
Child = LegacyColourCompatibility.ApplyWithDoubledAlpha(new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
}, lineColour)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HitTargetInsetContainer : Container
|
|
||||||
{
|
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
|
||||||
private readonly Container content;
|
|
||||||
|
|
||||||
private float hitPosition;
|
|
||||||
|
|
||||||
public HitTargetInsetContainer()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
InternalChild = content = new Container { RelativeSizeAxes = Axes.Both };
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
|
|
||||||
{
|
|
||||||
hitPosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.HitPosition)?.Value ?? Stage.HIT_TARGET_POSITION;
|
|
||||||
|
|
||||||
direction.BindTo(scrollingInfo.Direction);
|
|
||||||
direction.BindValueChanged(onDirectionChanged, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
|
||||||
{
|
|
||||||
content.Padding = direction.NewValue == ScrollingDirection.Up
|
|
||||||
? new MarginPadding { Top = hitPosition }
|
|
||||||
: new MarginPadding { Bottom = hitPosition };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
columnFlow = new ColumnFlow<Column>(definition)
|
columnFlow = new ColumnFlow<Column>(definition)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Left = COLUMN_SPACING, Right = COLUMN_SPACING },
|
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
this.hasColours = hasColours;
|
this.hasColours = hasColours;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, hasColours);
|
protected override IBeatmapSkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, hasColours);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestBeatmapSkin : LegacyBeatmapSkin
|
private class TestBeatmapSkin : LegacyBeatmapSkin
|
||||||
|
@ -29,12 +29,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
public class TestSceneSkinFallbacks : TestSceneOsuPlayer
|
public class TestSceneSkinFallbacks : TestSceneOsuPlayer
|
||||||
{
|
{
|
||||||
private readonly TestSource testUserSkin;
|
private readonly TestSource testUserSkin;
|
||||||
private readonly TestSource testBeatmapSkin;
|
private readonly BeatmapTestSource testBeatmapSkin;
|
||||||
|
|
||||||
public TestSceneSkinFallbacks()
|
public TestSceneSkinFallbacks()
|
||||||
{
|
{
|
||||||
testUserSkin = new TestSource("user");
|
testUserSkin = new TestSource("user");
|
||||||
testBeatmapSkin = new TestSource("beatmap");
|
testBeatmapSkin = new BeatmapTestSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -80,15 +80,15 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
public class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
|
public class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
|
||||||
{
|
{
|
||||||
private readonly ISkinSource skin;
|
private readonly IBeatmapSkin skin;
|
||||||
|
|
||||||
public CustomSkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock frameBasedClock, AudioManager audio, ISkinSource skin)
|
public CustomSkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock frameBasedClock, AudioManager audio, IBeatmapSkin skin)
|
||||||
: base(beatmap, storyboard, frameBasedClock, audio)
|
: base(beatmap, storyboard, frameBasedClock, audio)
|
||||||
{
|
{
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ISkin GetSkin() => skin;
|
protected override IBeatmapSkin GetSkin() => skin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkinProvidingPlayer : TestPlayer
|
public class SkinProvidingPlayer : TestPlayer
|
||||||
@ -112,6 +112,14 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class BeatmapTestSource : TestSource, IBeatmapSkin
|
||||||
|
{
|
||||||
|
public BeatmapTestSource()
|
||||||
|
: base("beatmap")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class TestSource : ISkinSource
|
public class TestSource : ISkinSource
|
||||||
{
|
{
|
||||||
private readonly string identifier;
|
private readonly string identifier;
|
||||||
|
@ -9,6 +9,7 @@ using osu.Game.Beatmaps.ControlPoints;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
{
|
{
|
||||||
@ -62,7 +63,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
drawableSpinner = new TestDrawableSpinner(spinner, auto)
|
drawableSpinner = new TestDrawableSpinner(spinner, auto)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Depth = depthIndex++
|
Depth = depthIndex++,
|
||||||
|
Scale = new Vector2(0.75f)
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var mod in SelectedMods.Value.OfType<IApplicableToDrawableHitObjects>())
|
foreach (var mod in SelectedMods.Value.OfType<IApplicableToDrawableHitObjects>())
|
||||||
|
@ -193,30 +193,46 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
|
|
||||||
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
|
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
|
||||||
|
|
||||||
public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[]
|
public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap)
|
||||||
{
|
{
|
||||||
new StatisticRow
|
var timedHitEvents = score.HitEvents.Where(e => e.HitObject is HitCircle && !(e.HitObject is SliderTailCircle)).ToList();
|
||||||
|
|
||||||
|
return new[]
|
||||||
{
|
{
|
||||||
Columns = new[]
|
new StatisticRow
|
||||||
{
|
{
|
||||||
new StatisticItem("Timing Distribution", new HitEventTimingDistributionGraph(score.HitEvents.Where(e => e.HitObject is HitCircle && !(e.HitObject is SliderTailCircle)).ToList())
|
Columns = new[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
new StatisticItem("Timing Distribution",
|
||||||
Height = 250
|
new HitEventTimingDistributionGraph(timedHitEvents)
|
||||||
}),
|
{
|
||||||
}
|
RelativeSizeAxes = Axes.X,
|
||||||
},
|
Height = 250
|
||||||
new StatisticRow
|
}),
|
||||||
{
|
}
|
||||||
Columns = new[]
|
},
|
||||||
|
new StatisticRow
|
||||||
{
|
{
|
||||||
new StatisticItem("Accuracy Heatmap", new AccuracyHeatmap(score, playableBeatmap)
|
Columns = new[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
new StatisticItem("Accuracy Heatmap", new AccuracyHeatmap(score, playableBeatmap)
|
||||||
Height = 250
|
{
|
||||||
}),
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 250
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new StatisticRow
|
||||||
|
{
|
||||||
|
Columns = new[]
|
||||||
|
{
|
||||||
|
new StatisticItem(string.Empty, new SimpleStatisticTable(3, new SimpleStatisticItem[]
|
||||||
|
{
|
||||||
|
new UnstableRate(timedHitEvents)
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
hitCircleSprite = new Sprite
|
hitCircleSprite = new Sprite
|
||||||
{
|
{
|
||||||
Texture = getTextureWithFallback(string.Empty),
|
Texture = getTextureWithFallback(string.Empty),
|
||||||
Colour = drawableObject.AccentColour.Value,
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
@ -107,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
state.BindValueChanged(updateState, true);
|
state.BindValueChanged(updateState, true);
|
||||||
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true);
|
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||||
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,11 +22,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
private DrawableSpinner drawableSpinner;
|
private DrawableSpinner drawableSpinner;
|
||||||
private Sprite disc;
|
private Sprite disc;
|
||||||
|
private Sprite metreSprite;
|
||||||
private Container metre;
|
private Container metre;
|
||||||
|
|
||||||
private const float background_y_offset = 20;
|
|
||||||
|
|
||||||
private const float sprite_scale = 1 / 1.6f;
|
private const float sprite_scale = 1 / 1.6f;
|
||||||
|
private const float final_metre_height = 692 * sprite_scale;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource source, DrawableHitObject drawableObject)
|
private void load(ISkinSource source, DrawableHitObject drawableObject)
|
||||||
@ -35,50 +35,58 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChild = new Container
|
||||||
{
|
{
|
||||||
new Sprite
|
// the old-style spinner relied heavily on absolute screen-space coordinate values.
|
||||||
|
// wrap everything in a container simulating absolute coords to preserve alignment
|
||||||
|
// as there are skins that depend on it.
|
||||||
|
Width = 640,
|
||||||
|
Height = 480,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
new Sprite
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
Texture = source.GetTexture("spinner-background"),
|
|
||||||
Y = background_y_offset,
|
|
||||||
Scale = new Vector2(sprite_scale)
|
|
||||||
},
|
|
||||||
disc = new Sprite
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Texture = source.GetTexture("spinner-circle"),
|
|
||||||
Scale = new Vector2(sprite_scale)
|
|
||||||
},
|
|
||||||
metre = new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
Y = background_y_offset,
|
|
||||||
Masking = true,
|
|
||||||
Child = new Sprite
|
|
||||||
{
|
{
|
||||||
Texture = source.GetTexture("spinner-metre"),
|
Anchor = Anchor.Centre,
|
||||||
Anchor = Anchor.BottomCentre,
|
Origin = Anchor.Centre,
|
||||||
Origin = Anchor.BottomCentre,
|
Texture = source.GetTexture("spinner-background"),
|
||||||
|
Scale = new Vector2(sprite_scale)
|
||||||
},
|
},
|
||||||
Scale = new Vector2(0.625f)
|
disc = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Texture = source.GetTexture("spinner-circle"),
|
||||||
|
Scale = new Vector2(sprite_scale)
|
||||||
|
},
|
||||||
|
metre = new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
// this anchor makes no sense, but that's what stable uses.
|
||||||
|
Anchor = Anchor.TopLeft,
|
||||||
|
Origin = Anchor.TopLeft,
|
||||||
|
// adjustment for stable (metre has additional offset)
|
||||||
|
Margin = new MarginPadding { Top = 20 },
|
||||||
|
Masking = true,
|
||||||
|
Child = metreSprite = new Sprite
|
||||||
|
{
|
||||||
|
Texture = source.GetTexture("spinner-metre"),
|
||||||
|
Anchor = Anchor.TopLeft,
|
||||||
|
Origin = Anchor.TopLeft,
|
||||||
|
Scale = new Vector2(0.625f)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2 metreFinalSize;
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
this.FadeOut();
|
this.FadeOut();
|
||||||
drawableSpinner.State.BindValueChanged(updateStateTransforms, true);
|
drawableSpinner.State.BindValueChanged(updateStateTransforms, true);
|
||||||
|
|
||||||
metreFinalSize = metre.Size = metre.Child.Size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStateTransforms(ValueChangedEvent<ArmedState> state)
|
private void updateStateTransforms(ValueChangedEvent<ArmedState> state)
|
||||||
@ -93,7 +101,16 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
disc.Rotation = drawableSpinner.RotationTracker.Rotation;
|
disc.Rotation = drawableSpinner.RotationTracker.Rotation;
|
||||||
metre.Height = getMetreHeight(drawableSpinner.Progress);
|
|
||||||
|
// careful: need to call this exactly once for all calculations in a frame
|
||||||
|
// as the function has a random factor in it
|
||||||
|
var metreHeight = getMetreHeight(drawableSpinner.Progress);
|
||||||
|
|
||||||
|
// hack to make the metre blink up from below than down from above.
|
||||||
|
// move down the container to be able to apply masking for the metre,
|
||||||
|
// and then move the sprite back up the same amount to keep its position absolute.
|
||||||
|
metre.Y = final_metre_height - metreHeight;
|
||||||
|
metreSprite.Y = -metre.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int total_bars = 10;
|
private const int total_bars = 10;
|
||||||
@ -108,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
if (RNG.NextBool(((int)progress % 10) / 10f))
|
if (RNG.NextBool(((int)progress % 10) / 10f))
|
||||||
barCount++;
|
barCount++;
|
||||||
|
|
||||||
return (float)barCount / total_bars * metreFinalSize.Y;
|
return (float)barCount / total_bars * final_metre_height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin, DrawableHitObject drawableObject)
|
private void load(ISkinSource skin, DrawableHitObject drawableObject)
|
||||||
{
|
{
|
||||||
animationContent.Colour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBall)?.Value ?? Color4.White;
|
var ballColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBall)?.Value ?? Color4.White;
|
||||||
|
|
||||||
InternalChildren = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
@ -39,11 +39,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
Texture = skin.GetTexture("sliderb-nd"),
|
Texture = skin.GetTexture("sliderb-nd"),
|
||||||
Colour = new Color4(5, 5, 5, 255),
|
Colour = new Color4(5, 5, 5, 255),
|
||||||
},
|
},
|
||||||
animationContent.With(d =>
|
LegacyColourCompatibility.ApplyWithDoubledAlpha(animationContent.With(d =>
|
||||||
{
|
{
|
||||||
d.Anchor = Anchor.Centre;
|
d.Anchor = Anchor.Centre;
|
||||||
d.Origin = Anchor.Centre;
|
d.Origin = Anchor.Centre;
|
||||||
}),
|
}), ballColour),
|
||||||
layerSpec = new Sprite
|
layerSpec = new Sprite
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
|
@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
|
|
||||||
private void updateAccentColour()
|
private void updateAccentColour()
|
||||||
{
|
{
|
||||||
backgroundLayer.Colour = accentColour;
|
backgroundLayer.Colour = LegacyColourCompatibility.DisallowZeroAlpha(accentColour);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,9 +76,11 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
|
|
||||||
private void updateAccentColour()
|
private void updateAccentColour()
|
||||||
{
|
{
|
||||||
headCircle.AccentColour = accentColour;
|
var colour = LegacyColourCompatibility.DisallowZeroAlpha(accentColour);
|
||||||
body.Colour = accentColour;
|
|
||||||
end.Colour = accentColour;
|
headCircle.AccentColour = colour;
|
||||||
|
body.Colour = colour;
|
||||||
|
end.Colour = colour;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Skinning
|
namespace osu.Game.Rulesets.Taiko.Skinning
|
||||||
@ -18,9 +19,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
AccentColour = component == TaikoSkinComponents.CentreHit
|
AccentColour = LegacyColourCompatibility.DisallowZeroAlpha(
|
||||||
? new Color4(235, 69, 44, 255)
|
component == TaikoSkinComponents.CentreHit
|
||||||
: new Color4(67, 142, 172, 255);
|
? new Color4(235, 69, 44, 255)
|
||||||
|
: new Color4(67, 142, 172, 255));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,19 +161,34 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
||||||
|
|
||||||
public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[]
|
public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap)
|
||||||
{
|
{
|
||||||
new StatisticRow
|
var timedHitEvents = score.HitEvents.Where(e => e.HitObject is Hit).ToList();
|
||||||
|
|
||||||
|
return new[]
|
||||||
{
|
{
|
||||||
Columns = new[]
|
new StatisticRow
|
||||||
{
|
{
|
||||||
new StatisticItem("Timing Distribution", new HitEventTimingDistributionGraph(score.HitEvents.Where(e => e.HitObject is Hit).ToList())
|
Columns = new[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
new StatisticItem("Timing Distribution", new HitEventTimingDistributionGraph(timedHitEvents)
|
||||||
Height = 250
|
{
|
||||||
}),
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 250
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new StatisticRow
|
||||||
|
{
|
||||||
|
Columns = new[]
|
||||||
|
{
|
||||||
|
new StatisticItem(string.Empty, new SimpleStatisticTable(3, new SimpleStatisticItem[]
|
||||||
|
{
|
||||||
|
new UnstableRate(timedHitEvents)
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
osu.Game.Tests/Beatmaps/BeatmapDifficultyManagerTest.cs
Normal file
32
osu.Game.Tests/Beatmaps/BeatmapDifficultyManagerTest.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Beatmaps
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class BeatmapDifficultyManagerTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestKeyEqualsWithDifferentModInstances()
|
||||||
|
{
|
||||||
|
var key1 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
|
||||||
|
var key2 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
|
||||||
|
|
||||||
|
Assert.That(key1, Is.EqualTo(key2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestKeyEqualsWithDifferentModOrder()
|
||||||
|
{
|
||||||
|
var key1 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
|
||||||
|
var key2 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
|
||||||
|
|
||||||
|
Assert.That(key1, Is.EqualTo(key2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs
Normal file
70
osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// 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 System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Catch;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Taiko;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Scoring.Legacy;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Beatmaps.Formats
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class LegacyScoreDecoderTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeManiaReplay()
|
||||||
|
{
|
||||||
|
var decoder = new TestLegacyScoreDecoder();
|
||||||
|
|
||||||
|
using (var resourceStream = TestResources.OpenResource("Replays/mania-replay.osr"))
|
||||||
|
{
|
||||||
|
var score = decoder.Parse(resourceStream);
|
||||||
|
|
||||||
|
Assert.AreEqual(3, score.ScoreInfo.Ruleset.ID);
|
||||||
|
|
||||||
|
Assert.AreEqual(2, score.ScoreInfo.Statistics[HitResult.Great]);
|
||||||
|
Assert.AreEqual(1, score.ScoreInfo.Statistics[HitResult.Good]);
|
||||||
|
|
||||||
|
Assert.AreEqual(829_931, score.ScoreInfo.TotalScore);
|
||||||
|
Assert.AreEqual(3, score.ScoreInfo.MaxCombo);
|
||||||
|
Assert.IsTrue(Precision.AlmostEquals(0.8889, score.ScoreInfo.Accuracy, 0.0001));
|
||||||
|
Assert.AreEqual(ScoreRank.B, score.ScoreInfo.Rank);
|
||||||
|
|
||||||
|
Assert.That(score.Replay.Frames, Is.Not.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestLegacyScoreDecoder : LegacyScoreDecoder
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<int, Ruleset> rulesets = new Ruleset[]
|
||||||
|
{
|
||||||
|
new OsuRuleset(),
|
||||||
|
new TaikoRuleset(),
|
||||||
|
new CatchRuleset(),
|
||||||
|
new ManiaRuleset()
|
||||||
|
}.ToDictionary(ruleset => ((ILegacyRuleset)ruleset).LegacyID);
|
||||||
|
|
||||||
|
protected override Ruleset GetRuleset(int rulesetId) => rulesets[rulesetId];
|
||||||
|
|
||||||
|
protected override WorkingBeatmap GetBeatmap(string md5Hash) => new TestWorkingBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo = new BeatmapInfo
|
||||||
|
{
|
||||||
|
MD5Hash = md5Hash,
|
||||||
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
BaseDifficulty = new BeatmapDifficulty()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -116,7 +116,7 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
AddAssert("sample playback rate matches mod rates", () => sample.Channel.AggregateFrequency.Value == expectedRate);
|
AddAssert("sample playback rate matches mod rates", () => sample.Channel.AggregateFrequency.Value == expectedRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestSkin : LegacySkin
|
private class TestSkin : LegacySkin, IBeatmapSkin
|
||||||
{
|
{
|
||||||
public TestSkin(string resourceName, AudioManager audioManager)
|
public TestSkin(string resourceName, AudioManager audioManager)
|
||||||
: base(DefaultLegacySkin.Info, new TestResourceStore(resourceName), audioManager, "skin.ini")
|
: base(DefaultLegacySkin.Info, new TestResourceStore(resourceName), audioManager, "skin.ini")
|
||||||
@ -156,7 +156,7 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
this.audio = audio;
|
this.audio = audio;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ISkin GetSkin() => new TestSkin("test-sample", audio);
|
protected override IBeatmapSkin GetSkin() => new TestSkin("test-sample", audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestDrawableStoryboardSample : DrawableStoryboardSample
|
private class TestDrawableStoryboardSample : DrawableStoryboardSample
|
||||||
|
BIN
osu.Game.Tests/Resources/Replays/mania-replay.osr
Normal file
BIN
osu.Game.Tests/Resources/Replays/mania-replay.osr
Normal file
Binary file not shown.
@ -1,5 +0,0 @@
|
|||||||
[General]
|
|
||||||
Version: latest
|
|
||||||
|
|
||||||
[Colours]
|
|
||||||
Combo1: 255,255,255,0
|
|
@ -108,15 +108,5 @@ namespace osu.Game.Tests.Skins
|
|||||||
using (var stream = new LineBufferedReader(resStream))
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
Assert.That(decoder.Decode(stream).LegacyVersion, Is.EqualTo(1.0m));
|
Assert.That(decoder.Decode(stream).LegacyVersion, Is.EqualTo(1.0m));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestDecodeColourWithZeroAlpha()
|
|
||||||
{
|
|
||||||
var decoder = new LegacySkinDecoder();
|
|
||||||
|
|
||||||
using (var resStream = TestResources.OpenResource("skin-zero-alpha-colour.ini"))
|
|
||||||
using (var stream = new LineBufferedReader(resStream))
|
|
||||||
Assert.That(decoder.Decode(stream).ComboColours[0].A, Is.EqualTo(1.0f));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Screens;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -212,6 +213,25 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
AddAssert("expanded panel still on screen", () => this.ChildrenOfType<ScorePanel>().Single(p => p.State == PanelState.Expanded).ScreenSpaceDrawQuad.TopLeft.X > 0);
|
AddAssert("expanded panel still on screen", () => this.ChildrenOfType<ScorePanel>().Single(p => p.State == PanelState.Expanded).ScreenSpaceDrawQuad.TopLeft.X > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDownloadButtonInitiallyDisabled()
|
||||||
|
{
|
||||||
|
TestResultsScreen screen = null;
|
||||||
|
|
||||||
|
AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen()));
|
||||||
|
|
||||||
|
AddAssert("download button is disabled", () => !screen.ChildrenOfType<DownloadButton>().Single().Enabled.Value);
|
||||||
|
|
||||||
|
AddStep("click contracted panel", () =>
|
||||||
|
{
|
||||||
|
var contractedPanel = this.ChildrenOfType<ScorePanel>().First(p => p.State == PanelState.Contracted && p.ScreenSpaceDrawQuad.TopLeft.X > screen.ScreenSpaceDrawQuad.TopLeft.X);
|
||||||
|
InputManager.MoveMouseTo(contractedPanel);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("download button is enabled", () => screen.ChildrenOfType<DownloadButton>().Single().Enabled.Value);
|
||||||
|
}
|
||||||
|
|
||||||
private class TestResultsContainer : Container
|
private class TestResultsContainer : Container
|
||||||
{
|
{
|
||||||
[Cached(typeof(Player))]
|
[Cached(typeof(Player))]
|
||||||
@ -255,6 +275,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
{
|
{
|
||||||
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo);
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo);
|
||||||
score.TotalScore += 10 - i;
|
score.TotalScore += 10 - i;
|
||||||
|
score.Hash = $"test{i}";
|
||||||
scores.Add(score);
|
scores.Add(score);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ using osu.Game.Screens.Ranking.Statistics;
|
|||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Ranking
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
{
|
{
|
||||||
public class TestSceneSimpleStatisticRow : OsuTestScene
|
public class TestSceneSimpleStatisticTable : OsuTestScene
|
||||||
{
|
{
|
||||||
private Container container;
|
private Container container;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
public void TestEmpty()
|
public void TestEmpty()
|
||||||
{
|
{
|
||||||
AddStep("create with no items",
|
AddStep("create with no items",
|
||||||
() => container.Add(new SimpleStatisticRow(2, Enumerable.Empty<SimpleStatisticItem>())));
|
() => container.Add(new SimpleStatisticTable(2, Enumerable.Empty<SimpleStatisticItem>())));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
Value = RNG.Next(100)
|
Value = RNG.Next(100)
|
||||||
});
|
});
|
||||||
|
|
||||||
container.Add(new SimpleStatisticRow(columnCount, items));
|
container.Add(new SimpleStatisticTable(columnCount, items));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -89,8 +89,14 @@ namespace osu.Game.Beatmaps
|
|||||||
if (tryGetExisting(beatmapInfo, rulesetInfo, mods, out var existing, out var key))
|
if (tryGetExisting(beatmapInfo, rulesetInfo, mods, out var existing, out var key))
|
||||||
return existing;
|
return existing;
|
||||||
|
|
||||||
return await Task.Factory.StartNew(() => computeDifficulty(key, beatmapInfo, rulesetInfo), cancellationToken,
|
return await Task.Factory.StartNew(() =>
|
||||||
TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
|
{
|
||||||
|
// Computation may have finished in a previous task.
|
||||||
|
if (tryGetExisting(beatmapInfo, rulesetInfo, mods, out existing, out _))
|
||||||
|
return existing;
|
||||||
|
|
||||||
|
return computeDifficulty(key, beatmapInfo, rulesetInfo);
|
||||||
|
}, cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -245,7 +251,7 @@ namespace osu.Game.Beatmaps
|
|||||||
updateScheduler?.Dispose();
|
updateScheduler?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly struct DifficultyCacheLookup : IEquatable<DifficultyCacheLookup>
|
public readonly struct DifficultyCacheLookup : IEquatable<DifficultyCacheLookup>
|
||||||
{
|
{
|
||||||
public readonly int BeatmapId;
|
public readonly int BeatmapId;
|
||||||
public readonly int RulesetId;
|
public readonly int RulesetId;
|
||||||
@ -261,7 +267,7 @@ namespace osu.Game.Beatmaps
|
|||||||
public bool Equals(DifficultyCacheLookup other)
|
public bool Equals(DifficultyCacheLookup other)
|
||||||
=> BeatmapId == other.BeatmapId
|
=> BeatmapId == other.BeatmapId
|
||||||
&& RulesetId == other.RulesetId
|
&& RulesetId == other.RulesetId
|
||||||
&& Mods.SequenceEqual(other.Mods);
|
&& Mods.Select(m => m.Acronym).SequenceEqual(other.Mods.Select(m => m.Acronym));
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
|
@ -140,7 +140,7 @@ namespace osu.Game.Beatmaps
|
|||||||
return storyboard;
|
return storyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ISkin GetSkin()
|
protected override IBeatmapSkin GetSkin()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -104,10 +104,6 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
byte alpha = split.Length == 4 ? byte.Parse(split[3]) : (byte)255;
|
byte alpha = split.Length == 4 ? byte.Parse(split[3]) : (byte)255;
|
||||||
|
|
||||||
if (alpha == 0)
|
|
||||||
alpha = 255;
|
|
||||||
|
|
||||||
colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), alpha);
|
colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), alpha);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@ -42,9 +42,9 @@ namespace osu.Game.Beatmaps
|
|||||||
Storyboard Storyboard { get; }
|
Storyboard Storyboard { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the <see cref="Skin"/> which this <see cref="WorkingBeatmap"/> provides.
|
/// Retrieves the <see cref="IBeatmapSkin"/> which this <see cref="WorkingBeatmap"/> provides.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ISkin Skin { get; }
|
IBeatmapSkin Skin { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>.
|
/// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>.
|
||||||
|
@ -38,5 +38,6 @@ namespace osu.Game.Beatmaps.Legacy
|
|||||||
Key1 = 1 << 26,
|
Key1 = 1 << 26,
|
||||||
Key3 = 1 << 27,
|
Key3 = 1 << 27,
|
||||||
Key2 = 1 << 28,
|
Key2 = 1 << 28,
|
||||||
|
Mirror = 1 << 30,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Beatmaps
|
|||||||
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
|
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
|
||||||
waveform = new RecyclableLazy<Waveform>(GetWaveform);
|
waveform = new RecyclableLazy<Waveform>(GetWaveform);
|
||||||
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
|
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
|
||||||
skin = new RecyclableLazy<ISkin>(GetSkin);
|
skin = new RecyclableLazy<IBeatmapSkin>(GetSkin);
|
||||||
|
|
||||||
total_count.Value++;
|
total_count.Value++;
|
||||||
}
|
}
|
||||||
@ -275,10 +275,10 @@ namespace osu.Game.Beatmaps
|
|||||||
private readonly RecyclableLazy<Storyboard> storyboard;
|
private readonly RecyclableLazy<Storyboard> storyboard;
|
||||||
|
|
||||||
public bool SkinLoaded => skin.IsResultAvailable;
|
public bool SkinLoaded => skin.IsResultAvailable;
|
||||||
public ISkin Skin => skin.Value;
|
public IBeatmapSkin Skin => skin.Value;
|
||||||
|
|
||||||
protected virtual ISkin GetSkin() => new DefaultSkin();
|
protected virtual IBeatmapSkin GetSkin() => new DefaultBeatmapSkin();
|
||||||
private readonly RecyclableLazy<ISkin> skin;
|
private readonly RecyclableLazy<IBeatmapSkin> skin;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transfer pieces of a beatmap to a new one, where possible, to save on loading.
|
/// Transfer pieces of a beatmap to a new one, where possible, to save on loading.
|
||||||
|
@ -119,7 +119,7 @@ namespace osu.Game.Online.Chat
|
|||||||
case "http":
|
case "http":
|
||||||
case "https":
|
case "https":
|
||||||
// length > 3 since all these links need another argument to work
|
// length > 3 since all these links need another argument to work
|
||||||
if (args.Length > 3 && (args[1] == "osu.ppy.sh" || args[1] == "new.ppy.sh"))
|
if (args.Length > 3 && args[1] == "osu.ppy.sh")
|
||||||
{
|
{
|
||||||
switch (args[2])
|
switch (args[2])
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,6 @@ using osu.Game.Replays.Legacy;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using SharpCompress.Compressors.LZMA;
|
using SharpCompress.Compressors.LZMA;
|
||||||
|
|
||||||
@ -123,12 +122,12 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
|
|
||||||
protected void CalculateAccuracy(ScoreInfo score)
|
protected void CalculateAccuracy(ScoreInfo score)
|
||||||
{
|
{
|
||||||
score.Statistics.TryGetValue(HitResult.Miss, out int countMiss);
|
int countMiss = score.GetCountMiss() ?? 0;
|
||||||
score.Statistics.TryGetValue(HitResult.Meh, out int count50);
|
int count50 = score.GetCount50() ?? 0;
|
||||||
score.Statistics.TryGetValue(HitResult.Good, out int count100);
|
int count100 = score.GetCount100() ?? 0;
|
||||||
score.Statistics.TryGetValue(HitResult.Great, out int count300);
|
int count300 = score.GetCount300() ?? 0;
|
||||||
score.Statistics.TryGetValue(HitResult.Perfect, out int countGeki);
|
int countGeki = score.GetCountGeki() ?? 0;
|
||||||
score.Statistics.TryGetValue(HitResult.Ok, out int countKatu);
|
int countKatu = score.GetCountKatu() ?? 0;
|
||||||
|
|
||||||
switch (score.Ruleset.ID)
|
switch (score.Ruleset.ID)
|
||||||
{
|
{
|
||||||
@ -241,12 +240,15 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
var diff = Parsing.ParseFloat(split[0]);
|
var diff = Parsing.ParseFloat(split[0]);
|
||||||
|
var mouseX = Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE);
|
||||||
|
var mouseY = Parsing.ParseFloat(split[2], Parsing.MAX_COORDINATE_VALUE);
|
||||||
|
|
||||||
lastTime += diff;
|
lastTime += diff;
|
||||||
|
|
||||||
if (i == 0 && diff == 0)
|
if (i < 2 && mouseX == 256 && mouseY == -500)
|
||||||
// osu-stable adds a zero-time frame before potentially valid negative user frames.
|
// at the start of the replay, stable places two replay frames, at time 0 and SkipBoundary - 1, respectively.
|
||||||
// we need to ignore this.
|
// both frames use a position of (256, -500).
|
||||||
|
// ignore these frames as they serve no real purpose (and can even mislead ruleset-specific handlers - see mania)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Todo: At some point we probably want to rewind and play back the negative-time frames
|
// Todo: At some point we probably want to rewind and play back the negative-time frames
|
||||||
@ -255,8 +257,8 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
currentFrame = convertFrame(new LegacyReplayFrame(lastTime,
|
currentFrame = convertFrame(new LegacyReplayFrame(lastTime,
|
||||||
Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE),
|
mouseX,
|
||||||
Parsing.ParseFloat(split[2], Parsing.MAX_COORDINATE_VALUE),
|
mouseY,
|
||||||
(ReplayButtonState)Parsing.ParseInt(split[3])), currentFrame);
|
(ReplayButtonState)Parsing.ParseInt(split[3])), currentFrame);
|
||||||
|
|
||||||
replay.Frames.Add(currentFrame);
|
replay.Frames.Add(currentFrame);
|
||||||
|
@ -205,6 +205,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
const int line_end_offset = 120;
|
const int line_end_offset = 120;
|
||||||
|
|
||||||
smallRing.Foreground.ResizeTo(1, line_duration, Easing.OutQuint);
|
smallRing.Foreground.ResizeTo(1, line_duration, Easing.OutQuint);
|
||||||
|
smallRing.Delay(400).FadeColour(Color4.Black, 300);
|
||||||
|
|
||||||
lineTopLeft.MoveTo(new Vector2(-line_end_offset, -line_end_offset), line_duration, Easing.OutQuint);
|
lineTopLeft.MoveTo(new Vector2(-line_end_offset, -line_end_offset), line_duration, Easing.OutQuint);
|
||||||
lineTopRight.MoveTo(new Vector2(line_end_offset, -line_end_offset), line_duration, Easing.OutQuint);
|
lineTopRight.MoveTo(new Vector2(line_end_offset, -line_end_offset), line_duration, Easing.OutQuint);
|
||||||
|
@ -74,23 +74,33 @@ namespace osu.Game.Screens.Ranking
|
|||||||
{
|
{
|
||||||
button.State.Value = state.NewValue;
|
button.State.Value = state.NewValue;
|
||||||
|
|
||||||
switch (replayAvailability)
|
updateTooltip();
|
||||||
{
|
|
||||||
case ReplayAvailability.Local:
|
|
||||||
button.TooltipText = @"watch replay";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ReplayAvailability.Online:
|
|
||||||
button.TooltipText = @"download replay";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
button.TooltipText = @"replay unavailable";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable;
|
Model.BindValueChanged(_ =>
|
||||||
|
{
|
||||||
|
button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable;
|
||||||
|
|
||||||
|
updateTooltip();
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTooltip()
|
||||||
|
{
|
||||||
|
switch (replayAvailability)
|
||||||
|
{
|
||||||
|
case ReplayAvailability.Local:
|
||||||
|
button.TooltipText = @"watch replay";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ReplayAvailability.Online:
|
||||||
|
button.TooltipText = @"download replay";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
button.TooltipText = @"replay unavailable";
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ReplayAvailability
|
private enum ReplayAvailability
|
||||||
|
@ -41,13 +41,14 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
{
|
{
|
||||||
Text = Name,
|
Text = Name,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft
|
Origin = Anchor.CentreLeft,
|
||||||
|
Font = OsuFont.GetFont(size: 14)
|
||||||
},
|
},
|
||||||
value = new OsuSpriteText
|
value = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Font = OsuFont.Torus.With(weight: FontWeight.Bold)
|
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -13,10 +14,10 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
namespace osu.Game.Screens.Ranking.Statistics
|
namespace osu.Game.Screens.Ranking.Statistics
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a statistic row with simple statistics (ones that only need textual display).
|
/// Represents a table with simple statistics (ones that only need textual display).
|
||||||
/// Richer visualisations should be done with <see cref="StatisticRow"/>s and <see cref="StatisticItem"/>s.
|
/// Richer visualisations should be done with <see cref="StatisticRow"/>s and <see cref="StatisticItem"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SimpleStatisticRow : CompositeDrawable
|
public class SimpleStatisticTable : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly SimpleStatisticItem[] items;
|
private readonly SimpleStatisticItem[] items;
|
||||||
private readonly int columnCount;
|
private readonly int columnCount;
|
||||||
@ -28,7 +29,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="columnCount">The number of columns to layout the <paramref name="items"/> into.</param>
|
/// <param name="columnCount">The number of columns to layout the <paramref name="items"/> into.</param>
|
||||||
/// <param name="items">The <see cref="SimpleStatisticItem"/>s to display in this row.</param>
|
/// <param name="items">The <see cref="SimpleStatisticItem"/>s to display in this row.</param>
|
||||||
public SimpleStatisticRow(int columnCount, IEnumerable<SimpleStatisticItem> items)
|
public SimpleStatisticTable(int columnCount, [ItemNotNull] IEnumerable<SimpleStatisticItem> items)
|
||||||
{
|
{
|
||||||
if (columnCount < 1)
|
if (columnCount < 1)
|
||||||
throw new ArgumentOutOfRangeException(nameof(columnCount));
|
throw new ArgumentOutOfRangeException(nameof(columnCount));
|
@ -32,33 +32,9 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Content = new[]
|
Content = new[]
|
||||||
{
|
{
|
||||||
new Drawable[]
|
new[]
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
createHeader(item)
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Spacing = new Vector2(5, 0),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Circle
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Height = 9,
|
|
||||||
Width = 4,
|
|
||||||
Colour = Color4Extensions.FromHex("#00FFAA")
|
|
||||||
},
|
|
||||||
new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Text = item.Name,
|
|
||||||
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
@ -78,5 +54,37 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Drawable createHeader(StatisticItem item)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(item.Name))
|
||||||
|
return Empty();
|
||||||
|
|
||||||
|
return new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(5, 0),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Circle
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Height = 9,
|
||||||
|
Width = 4,
|
||||||
|
Colour = Color4Extensions.FromHex("#00FFAA")
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Text = item.Name,
|
||||||
|
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="StatisticItem"/>, to be displayed inside a <see cref="StatisticRow"/> in the results screen.
|
/// Creates a new <see cref="StatisticItem"/>, to be displayed inside a <see cref="StatisticRow"/> in the results screen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name of the item.</param>
|
/// <param name="name">The name of the item. Can be <see cref="string.Empty"/> to hide the item header.</param>
|
||||||
/// <param name="content">The <see cref="Drawable"/> content to be displayed.</param>
|
/// <param name="content">The <see cref="Drawable"/> content to be displayed.</param>
|
||||||
/// <param name="dimension">The <see cref="Dimension"/> of this item. This can be thought of as the column dimension of an encompassing <see cref="GridContainer"/>.</param>
|
/// <param name="dimension">The <see cref="Dimension"/> of this item. This can be thought of as the column dimension of an encompassing <see cref="GridContainer"/>.</param>
|
||||||
public StatisticItem([NotNull] string name, [NotNull] Drawable content, [CanBeNull] Dimension dimension = null)
|
public StatisticItem([NotNull] string name, [NotNull] Drawable content, [CanBeNull] Dimension dimension = null)
|
||||||
|
@ -94,14 +94,15 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(30, 15),
|
Spacing = new Vector2(30, 15),
|
||||||
|
Alpha = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var row in newScore.Ruleset.CreateInstance().CreateStatisticsForScore(newScore, playableBeatmap))
|
foreach (var row in newScore.Ruleset.CreateInstance().CreateStatisticsForScore(newScore, playableBeatmap))
|
||||||
{
|
{
|
||||||
rows.Add(new GridContainer
|
rows.Add(new GridContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.TopCentre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Content = new[]
|
Content = new[]
|
||||||
@ -125,6 +126,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
|
|
||||||
spinner.Hide();
|
spinner.Hide();
|
||||||
content.Add(d);
|
content.Add(d);
|
||||||
|
d.FadeIn(250, Easing.OutQuint);
|
||||||
}, localCancellationSource.Token);
|
}, localCancellationSource.Token);
|
||||||
}), localCancellationSource.Token);
|
}), localCancellationSource.Token);
|
||||||
}
|
}
|
||||||
|
39
osu.Game/Screens/Ranking/Statistics/UnstableRate.cs
Normal file
39
osu.Game/Screens/Ranking/Statistics/UnstableRate.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Ranking.Statistics
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Displays the unstable rate statistic for a given play.
|
||||||
|
/// </summary>
|
||||||
|
public class UnstableRate : SimpleStatisticItem<double>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and computes an <see cref="UnstableRate"/> statistic.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitEvents">Sequence of <see cref="HitEvent"/>s to calculate the unstable rate based on.</param>
|
||||||
|
public UnstableRate(IEnumerable<HitEvent> hitEvents)
|
||||||
|
: base("Unstable Rate")
|
||||||
|
{
|
||||||
|
var timeOffsets = hitEvents.Select(ev => ev.TimeOffset).ToArray();
|
||||||
|
Value = 10 * standardDeviation(timeOffsets);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double standardDeviation(double[] timeOffsets)
|
||||||
|
{
|
||||||
|
if (timeOffsets.Length == 0)
|
||||||
|
return double.NaN;
|
||||||
|
|
||||||
|
var mean = timeOffsets.Average();
|
||||||
|
var squares = timeOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum();
|
||||||
|
return Math.Sqrt(squares / timeOffsets.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string DisplayValue(double value) => double.IsNaN(value) ? "(not available)" : value.ToString("N2");
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ namespace osu.Game.Skinning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A container which overrides existing skin options with beatmap-local values.
|
/// A container which overrides existing skin options with beatmap-local values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BeatmapSkinProvidingContainer : SkinProvidingContainer
|
public class BeatmapSkinProvidingContainer : SkinProvidingContainer, IBeatmapSkin
|
||||||
{
|
{
|
||||||
private readonly Bindable<bool> beatmapSkins = new Bindable<bool>();
|
private readonly Bindable<bool> beatmapSkins = new Bindable<bool>();
|
||||||
private readonly Bindable<bool> beatmapHitsounds = new Bindable<bool>();
|
private readonly Bindable<bool> beatmapHitsounds = new Bindable<bool>();
|
||||||
@ -21,7 +21,7 @@ namespace osu.Game.Skinning
|
|||||||
protected override bool AllowTextureLookup(string componentName) => beatmapSkins.Value;
|
protected override bool AllowTextureLookup(string componentName) => beatmapSkins.Value;
|
||||||
protected override bool AllowSampleLookup(ISampleInfo componentName) => beatmapHitsounds.Value;
|
protected override bool AllowSampleLookup(ISampleInfo componentName) => beatmapHitsounds.Value;
|
||||||
|
|
||||||
public BeatmapSkinProvidingContainer(ISkin skin)
|
public BeatmapSkinProvidingContainer(IBeatmapSkin skin)
|
||||||
: base(skin)
|
: base(skin)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
9
osu.Game/Skinning/DefaultBeatmapSkin.cs
Normal file
9
osu.Game/Skinning/DefaultBeatmapSkin.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
public class DefaultBeatmapSkin : DefaultSkin, IBeatmapSkin
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
12
osu.Game/Skinning/IBeatmapSkin.cs
Normal file
12
osu.Game/Skinning/IBeatmapSkin.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Marker interface for skins that originate from beatmaps.
|
||||||
|
/// </summary>
|
||||||
|
public interface IBeatmapSkin : ISkin
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ using osu.Game.Rulesets.Objects.Legacy;
|
|||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
public class LegacyBeatmapSkin : LegacySkin
|
public class LegacyBeatmapSkin : LegacySkin, IBeatmapSkin
|
||||||
{
|
{
|
||||||
protected override bool AllowManiaSkin => false;
|
protected override bool AllowManiaSkin => false;
|
||||||
protected override bool UseCustomSampleBanks => true;
|
protected override bool UseCustomSampleBanks => true;
|
||||||
|
46
osu.Game/Skinning/LegacyColourCompatibility.cs
Normal file
46
osu.Game/Skinning/LegacyColourCompatibility.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Compatibility methods to convert osu!stable colours to osu!lazer-compatible ones. Should be used for legacy skins only.
|
||||||
|
/// </summary>
|
||||||
|
public static class LegacyColourCompatibility
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Forces an alpha of 1 if a given <see cref="Color4"/> is fully transparent.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is equivalent to setting colour post-constructor in osu!stable.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="colour">The <see cref="Color4"/> to disallow zero alpha on.</param>
|
||||||
|
/// <returns>The resultant <see cref="Color4"/>.</returns>
|
||||||
|
public static Color4 DisallowZeroAlpha(Color4 colour)
|
||||||
|
{
|
||||||
|
if (colour.A == 0)
|
||||||
|
colour.A = 1;
|
||||||
|
return colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies a <see cref="Color4"/> to a <see cref="Drawable"/>, doubling the alpha value into the <see cref="Drawable.Alpha"/> property.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is equivalent to setting colour in the constructor in osu!stable.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="drawable">The <see cref="Drawable"/> to apply the colour to.</param>
|
||||||
|
/// <param name="colour">The <see cref="Color4"/> to apply.</param>
|
||||||
|
/// <returns>The given <paramref name="drawable"/>.</returns>
|
||||||
|
public static T ApplyWithDoubledAlpha<T>(T drawable, Color4 colour)
|
||||||
|
where T : Drawable
|
||||||
|
{
|
||||||
|
drawable.Alpha = colour.A;
|
||||||
|
drawable.Colour = DisallowZeroAlpha(colour);
|
||||||
|
return drawable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,10 +31,12 @@ namespace osu.Game.Skinning
|
|||||||
public readonly float[] ColumnSpacing;
|
public readonly float[] ColumnSpacing;
|
||||||
public readonly float[] ColumnWidth;
|
public readonly float[] ColumnWidth;
|
||||||
public readonly float[] ExplosionWidth;
|
public readonly float[] ExplosionWidth;
|
||||||
|
public readonly float[] HoldNoteLightWidth;
|
||||||
|
|
||||||
public float HitPosition = (480 - 402) * POSITION_SCALE_FACTOR;
|
public float HitPosition = (480 - 402) * POSITION_SCALE_FACTOR;
|
||||||
public float LightPosition = (480 - 413) * POSITION_SCALE_FACTOR;
|
public float LightPosition = (480 - 413) * POSITION_SCALE_FACTOR;
|
||||||
public bool ShowJudgementLine = true;
|
public bool ShowJudgementLine = true;
|
||||||
|
public bool KeysUnderNotes;
|
||||||
|
|
||||||
public LegacyManiaSkinConfiguration(int keys)
|
public LegacyManiaSkinConfiguration(int keys)
|
||||||
{
|
{
|
||||||
@ -44,6 +46,7 @@ namespace osu.Game.Skinning
|
|||||||
ColumnSpacing = new float[keys - 1];
|
ColumnSpacing = new float[keys - 1];
|
||||||
ColumnWidth = new float[keys];
|
ColumnWidth = new float[keys];
|
||||||
ExplosionWidth = new float[keys];
|
ExplosionWidth = new float[keys];
|
||||||
|
HoldNoteLightWidth = new float[keys];
|
||||||
|
|
||||||
ColumnLineWidth.AsSpan().Fill(2);
|
ColumnLineWidth.AsSpan().Fill(2);
|
||||||
ColumnWidth.AsSpan().Fill(DEFAULT_COLUMN_SIZE);
|
ColumnWidth.AsSpan().Fill(DEFAULT_COLUMN_SIZE);
|
||||||
|
@ -34,6 +34,8 @@ namespace osu.Game.Skinning
|
|||||||
HoldNoteHeadImage,
|
HoldNoteHeadImage,
|
||||||
HoldNoteTailImage,
|
HoldNoteTailImage,
|
||||||
HoldNoteBodyImage,
|
HoldNoteBodyImage,
|
||||||
|
HoldNoteLightImage,
|
||||||
|
HoldNoteLightScale,
|
||||||
ExplosionImage,
|
ExplosionImage,
|
||||||
ExplosionScale,
|
ExplosionScale,
|
||||||
ColumnLineColour,
|
ColumnLineColour,
|
||||||
@ -50,5 +52,6 @@ namespace osu.Game.Skinning
|
|||||||
Hit100,
|
Hit100,
|
||||||
Hit50,
|
Hit50,
|
||||||
Hit0,
|
Hit0,
|
||||||
|
KeysUnderNotes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,10 +97,18 @@ namespace osu.Game.Skinning
|
|||||||
currentConfig.ShowJudgementLine = pair.Value == "1";
|
currentConfig.ShowJudgementLine = pair.Value == "1";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "KeysUnderNotes":
|
||||||
|
currentConfig.KeysUnderNotes = pair.Value == "1";
|
||||||
|
break;
|
||||||
|
|
||||||
case "LightingNWidth":
|
case "LightingNWidth":
|
||||||
parseArrayValue(pair.Value, currentConfig.ExplosionWidth);
|
parseArrayValue(pair.Value, currentConfig.ExplosionWidth);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "LightingLWidth":
|
||||||
|
parseArrayValue(pair.Value, currentConfig.HoldNoteLightWidth);
|
||||||
|
break;
|
||||||
|
|
||||||
case "WidthForNoteHeightScale":
|
case "WidthForNoteHeightScale":
|
||||||
float minWidth = float.Parse(pair.Value, CultureInfo.InvariantCulture) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
|
float minWidth = float.Parse(pair.Value, CultureInfo.InvariantCulture) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
|
||||||
if (minWidth > 0)
|
if (minWidth > 0)
|
||||||
@ -116,6 +124,7 @@ namespace osu.Game.Skinning
|
|||||||
case string _ when pair.Key.StartsWith("KeyImage"):
|
case string _ when pair.Key.StartsWith("KeyImage"):
|
||||||
case string _ when pair.Key.StartsWith("Hit"):
|
case string _ when pair.Key.StartsWith("Hit"):
|
||||||
case string _ when pair.Key.StartsWith("Stage"):
|
case string _ when pair.Key.StartsWith("Stage"):
|
||||||
|
case string _ when pair.Key.StartsWith("Lighting"):
|
||||||
currentConfig.ImageLookups[pair.Key] = pair.Value;
|
currentConfig.ImageLookups[pair.Key] = pair.Value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,9 @@ namespace osu.Game.Skinning
|
|||||||
case LegacyManiaSkinConfigurationLookups.ShowJudgementLine:
|
case LegacyManiaSkinConfigurationLookups.ShowJudgementLine:
|
||||||
return SkinUtils.As<TValue>(new Bindable<bool>(existing.ShowJudgementLine));
|
return SkinUtils.As<TValue>(new Bindable<bool>(existing.ShowJudgementLine));
|
||||||
|
|
||||||
|
case LegacyManiaSkinConfigurationLookups.ExplosionImage:
|
||||||
|
return SkinUtils.As<TValue>(getManiaImage(existing, "LightingN"));
|
||||||
|
|
||||||
case LegacyManiaSkinConfigurationLookups.ExplosionScale:
|
case LegacyManiaSkinConfigurationLookups.ExplosionScale:
|
||||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||||
|
|
||||||
@ -217,6 +220,20 @@ namespace osu.Game.Skinning
|
|||||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||||
return SkinUtils.As<TValue>(getManiaImage(existing, $"NoteImage{maniaLookup.TargetColumn}L"));
|
return SkinUtils.As<TValue>(getManiaImage(existing, $"NoteImage{maniaLookup.TargetColumn}L"));
|
||||||
|
|
||||||
|
case LegacyManiaSkinConfigurationLookups.HoldNoteLightImage:
|
||||||
|
return SkinUtils.As<TValue>(getManiaImage(existing, "LightingL"));
|
||||||
|
|
||||||
|
case LegacyManiaSkinConfigurationLookups.HoldNoteLightScale:
|
||||||
|
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||||
|
|
||||||
|
if (GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value < 2.5m)
|
||||||
|
return SkinUtils.As<TValue>(new Bindable<float>(1));
|
||||||
|
|
||||||
|
if (existing.HoldNoteLightWidth[maniaLookup.TargetColumn.Value] != 0)
|
||||||
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.HoldNoteLightWidth[maniaLookup.TargetColumn.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE));
|
||||||
|
|
||||||
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.ColumnWidth[maniaLookup.TargetColumn.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE));
|
||||||
|
|
||||||
case LegacyManiaSkinConfigurationLookups.KeyImage:
|
case LegacyManiaSkinConfigurationLookups.KeyImage:
|
||||||
Debug.Assert(maniaLookup.TargetColumn != null);
|
Debug.Assert(maniaLookup.TargetColumn != null);
|
||||||
return SkinUtils.As<TValue>(getManiaImage(existing, $"KeyImage{maniaLookup.TargetColumn}"));
|
return SkinUtils.As<TValue>(getManiaImage(existing, $"KeyImage{maniaLookup.TargetColumn}"));
|
||||||
@ -255,6 +272,9 @@ namespace osu.Game.Skinning
|
|||||||
case LegacyManiaSkinConfigurationLookups.Hit300:
|
case LegacyManiaSkinConfigurationLookups.Hit300:
|
||||||
case LegacyManiaSkinConfigurationLookups.Hit300g:
|
case LegacyManiaSkinConfigurationLookups.Hit300g:
|
||||||
return SkinUtils.As<TValue>(getManiaImage(existing, maniaLookup.Lookup.ToString()));
|
return SkinUtils.As<TValue>(getManiaImage(existing, maniaLookup.Lookup.ToString()));
|
||||||
|
|
||||||
|
case LegacyManiaSkinConfigurationLookups.KeysUnderNotes:
|
||||||
|
return SkinUtils.As<TValue>(new Bindable<bool>(existing.KeysUnderNotes));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -188,7 +188,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
this.resourceStore = resourceStore;
|
this.resourceStore = resourceStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ISkin GetSkin() => new LegacyBeatmapSkin(skinBeatmapInfo, resourceStore, AudioManager);
|
protected override IBeatmapSkin GetSkin() => new LegacyBeatmapSkin(skinBeatmapInfo, resourceStore, AudioManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user