1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 17:43:05 +08:00

Merge remote-tracking branch 'upstream/master' into non-timeoffset-judgements

This commit is contained in:
Dean Herbert 2019-09-03 13:07:58 +09:00
commit 05ed9d3802
53 changed files with 391 additions and 129 deletions

View File

@ -60,7 +60,7 @@
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.830.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.903.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.830.1" />
</ItemGroup>
</Project>

View File

@ -82,11 +82,11 @@ namespace osu.Game.Rulesets.Catch.Tests
remove { }
}
public Drawable GetDrawableComponent(string componentName)
public Drawable GetDrawableComponent(ISkinComponent component)
{
switch (componentName)
switch (component.LookupName)
{
case "Play/Catch/fruit-catcher-idle":
case "Gameplay/Catch/fruit-catcher-idle":
return new CatcherCustomSkin();
}

View File

@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Catch
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
public const string SHORT_NAME = "fruits";
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{
new KeyBinding(InputKey.Z, CatchAction.MoveLeft),
@ -117,7 +119,7 @@ namespace osu.Game.Rulesets.Catch
public override string Description => "osu!catch";
public override string ShortName => "fruits";
public override string ShortName => SHORT_NAME;
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetCatch };

View File

@ -0,0 +1,19 @@
// 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.Skinning;
namespace osu.Game.Rulesets.Catch
{
public class CatchSkinComponent : GameplaySkinComponent<CatchSkinComponents>
{
public CatchSkinComponent(CatchSkinComponents component)
: base(component)
{
}
protected override string RulesetPrefix => CatchRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower();
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Rulesets.Catch
{
public enum CatchSkinComponents
{
Catcher
}
}

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.UI
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new SkinnableSprite(@"Play/Catch/fruit-catcher-idle")
InternalChild = new SkinnableSprite(new CatchSkinComponent(CatchSkinComponents.Catcher))
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,

View File

@ -35,6 +35,8 @@ namespace osu.Game.Rulesets.Mania
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
public const string SHORT_NAME = "mania";
public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this);
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
@ -163,7 +165,7 @@ namespace osu.Game.Rulesets.Mania
public override string Description => "osu!mania";
public override string ShortName => "mania";
public override string ShortName => SHORT_NAME;
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania };

View File

@ -0,0 +1,19 @@
// 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.Skinning;
namespace osu.Game.Rulesets.Mania
{
public class ManiaSkinComponent : GameplaySkinComponent<ManiaSkinComponents>
{
public ManiaSkinComponent(ManiaSkinComponents component)
: base(component)
{
}
protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower();
}
}

View 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.Rulesets.Mania
{
public enum ManiaSkinComponents
{
}
}

View File

@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.Osu.Tests
this.identifier = identifier;
}
public Drawable GetDrawableComponent(string componentName)
public Drawable GetDrawableComponent(ISkinComponent component)
{
if (!enabled) return null;

View File

@ -188,7 +188,7 @@ namespace osu.Game.Rulesets.Osu.Mods
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get("Play/osu/blinds-panel");
Texture = textures.Get("Gameplay/osu/blinds-panel");
}
}
}

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
Origin = Anchor.Centre;
Child = new SkinnableDrawable("Play/osu/followpoint", _ => new Container
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.FollowPoint), _ => new Container
{
Masking = true,
AutoSizeAxes = Axes.Both,

View File

@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return true;
},
},
mainContent = new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)),
mainContent = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.HitCircle), _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)),
ApproachCircle = new ApproachCircle
{
Alpha = 0,

View File

@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Blending = BlendingParameters.Additive;
Origin = Anchor.Centre;
InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon
InternalChild = scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon
{
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.Solid.ChevronRight,

View File

@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Origin = Anchor.Centre;
InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new CircularContainer
InternalChild = scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderScorePoint), _ => new CircularContainer
{
Masking = true,
Origin = Anchor.Centre,

View File

@ -31,13 +31,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private class SkinnableApproachCircle : SkinnableSprite
{
public SkinnableApproachCircle()
: base("Play/osu/approachcircle")
: base(new OsuSkinComponent(OsuSkinComponents.ApproachCircle))
{
}
protected override Drawable CreateDefault(string name)
protected override Drawable CreateDefault(ISkinComponent component)
{
var drawable = base.CreateDefault(name);
var drawable = base.CreateDefault(component);
// account for the sprite being used for the default approach circle being taken from stable,
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.

View File

@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = textures.Get(@"Play/osu/disc"),
Texture = textures.Get(@"Gameplay/osu/disc"),
},
new TrianglesPiece
{

View File

@ -3,7 +3,6 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@ -20,12 +19,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Blending = BlendingParameters.Additive;
Alpha = 0;
Child = new SkinnableDrawable("Play/osu/hitcircle-explode", _ => new TrianglesPiece
Child = new TrianglesPiece
{
Blending = BlendingParameters.Additive,
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
}, s => s.GetTexture("Play/osu/hitcircle") == null);
};
}
}
}

View File

@ -5,7 +5,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osuTK;
using osu.Framework.Graphics.Shapes;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@ -21,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Blending = BlendingParameters.Additive;
Alpha = 0;
Child = new SkinnableDrawable("Play/osu/hitcircle-flash", name => new CircularContainer
Child = new CircularContainer
{
Masking = true,
RelativeSizeAxes = Axes.Both,
@ -29,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
RelativeSizeAxes = Axes.Both
}
}, s => s.GetTexture("Play/osu/hitcircle") == null);
};
}
}
}

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@ -22,14 +21,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Child = new SkinnableDrawable("Play/osu/ring-glow", name => new Sprite
Child = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = textures.Get(name),
Texture = textures.Get("ring-glow"),
Blending = BlendingParameters.Additive,
Alpha = 0.5f
}, s => s.GetTexture("Play/osu/hitcircle") == null);
};
}
}
}

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Children = new Drawable[]
{
new SkinnableDrawable("Play/osu/number-glow", name => new CircularContainer
new CircularContainer
{
Masking = true,
Origin = Anchor.Centre,
@ -41,8 +41,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Colour = Color4.White.Opacity(0.5f),
},
Child = new Box()
}, s => s.GetTexture("Play/osu/hitcircle") == null),
number = new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText
},
number = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText
{
Font = OsuFont.Numeric.With(size: 40),
UseFullGlyphHeight = false,

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics.Containers;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
InternalChild = new SkinnableDrawable("Play/osu/hitcircleoverlay", _ => new Container
InternalChild = new Container
{
Masking = true,
CornerRadius = Size.X / 2,
@ -35,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
RelativeSizeAxes = Axes.Both
}
}
});
};
}
}
}

View File

@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new DefaultFollowCircle()),
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderFollowCircle), _ => new DefaultFollowCircle()),
},
new CircularContainer
{
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Child = new SkinnableDrawable("Play/osu/sliderball", _ => new DefaultSliderBall()),
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBall), _ => new DefaultSliderBall()),
}
}
};

View File

@ -35,6 +35,8 @@ namespace osu.Game.Rulesets.Osu
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
public const string SHORT_NAME = "osu";
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{
new KeyBinding(InputKey.Z, OsuAction.LeftButton),
@ -161,7 +163,7 @@ namespace osu.Game.Rulesets.Osu
public override string Description => "osu!";
public override string ShortName => "osu";
public override string ShortName => SHORT_NAME;
public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this);

View File

@ -0,0 +1,19 @@
// 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.Skinning;
namespace osu.Game.Rulesets.Osu
{
public class OsuSkinComponent : GameplaySkinComponent<OsuSkinComponents>
{
public OsuSkinComponent(OsuSkinComponents component)
: base(component)
{
}
protected override string RulesetPrefix => OsuRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower();
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Rulesets.Osu
{
public enum OsuSkinComponents
{
HitCircle,
FollowPoint,
Cursor,
SliderScorePoint,
ApproachCircle,
ReverseArrow,
HitCircleText,
SliderFollowCircle,
SliderBall
}
}

View File

@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText
new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText
{
Font = OsuFont.Numeric.With(size: 40),
UseFullGlyphHeight = false,

View File

@ -55,14 +55,17 @@ namespace osu.Game.Rulesets.Osu.Skinning
hasHitCircle = new Lazy<bool>(() => source.GetTexture("hitcircle") != null);
}
public Drawable GetDrawableComponent(string componentName)
public Drawable GetDrawableComponent(ISkinComponent component)
{
switch (componentName)
{
case "Play/osu/sliderfollowcircle":
return this.GetAnimation(componentName, true, true);
if (!(component is OsuSkinComponent osuComponent))
return null;
case "Play/osu/sliderball":
switch (osuComponent.Component)
{
case OsuSkinComponents.SliderFollowCircle:
return this.GetAnimation("sliderfollowcircle", true, true);
case OsuSkinComponents.SliderBall:
var sliderBallContent = this.GetAnimation("sliderb", true, true, "");
if (sliderBallContent != null)
@ -80,20 +83,19 @@ namespace osu.Game.Rulesets.Osu.Skinning
return null;
case "Play/osu/hitcircle":
case OsuSkinComponents.HitCircle:
if (hasHitCircle.Value)
return new LegacyMainCirclePiece();
return null;
case "Play/osu/cursor":
case OsuSkinComponents.Cursor:
if (source.GetTexture("cursor") != null)
return new LegacyCursor();
return null;
case "Play/osu/number-text":
case OsuSkinComponents.HitCircleText:
string font = GetValue<SkinConfiguration, string>(config => config.HitCircleFont);
var overlap = GetValue<SkinConfiguration, float>(config => config.HitCircleOverlap);

View File

@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Child = scaleTarget = new SkinnableDrawable("Play/osu/cursor", _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling)
Child = scaleTarget = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,

View File

@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Taiko
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableTaikoRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
public const string SHORT_NAME = "taiko";
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{
new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre),
@ -116,7 +118,7 @@ namespace osu.Game.Rulesets.Taiko
public override string Description => "osu!taiko";
public override string ShortName => "taiko";
public override string ShortName => SHORT_NAME;
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetTaiko };

View File

@ -0,0 +1,19 @@
// 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.Skinning;
namespace osu.Game.Rulesets.Taiko
{
public class TaikoSkinComponent : GameplaySkinComponent<TaikoSkinComponents>
{
public TaikoSkinComponent(TaikoSkinComponents component)
: base(component)
{
}
protected override string RulesetPrefix => TaikoRuleset.SHORT_NAME;
protected override string ComponentName => Component.ToString().ToLower();
}
}

View 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.Rulesets.Taiko
{
public enum TaikoSkinComponents
{
}
}

View File

@ -132,10 +132,10 @@ namespace osu.Game.Rulesets.Taiko.UI
[BackgroundDependencyLoader]
private void load(TextureStore textures, OsuColour colours)
{
rim.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer");
rimHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer-hit");
centre.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner");
centreHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner-hit");
rim.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-outer");
rimHit.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-outer-hit");
centre.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-inner");
centreHit.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-inner-hit");
rimHit.Colour = colours.Blue;
centreHit.Colour = colours.Pink;

View File

@ -137,8 +137,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public new Drawable Drawable => base.Drawable;
public ExposedSkinnableDrawable(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(name, defaultImplementation, allowFallback, confineMode)
public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode)
{
}
}
@ -206,8 +206,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public new Drawable Drawable => base.Drawable;
public int SkinChangedCount { get; private set; }
public SkinConsumer(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null)
: base(name, defaultImplementation, allowFallback)
public SkinConsumer(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null)
: base(new TestSkinComponent(name), defaultImplementation, allowFallback)
{
}
@ -243,8 +243,8 @@ namespace osu.Game.Tests.Visual.Gameplay
this.size = size;
}
public Drawable GetDrawableComponent(string componentName) =>
componentName == "available"
public Drawable GetDrawableComponent(ISkinComponent componentName) =>
componentName.LookupName == "available"
? new DrawWidthBox
{
Colour = Color4.Yellow,
@ -261,7 +261,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private class SecondarySource : ISkin
{
public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox();
public Drawable GetDrawableComponent(ISkinComponent componentName) => new SecondarySourceBox();
public Texture GetTexture(string componentName) => throw new NotImplementedException();
@ -272,7 +272,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private class SkinSourceContainer : Container, ISkin
{
public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox();
public Drawable GetDrawableComponent(ISkinComponent componentName) => new BaseSourceBox();
public Texture GetTexture(string componentName) => throw new NotImplementedException();
@ -280,5 +280,19 @@ namespace osu.Game.Tests.Visual.Gameplay
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
}
private class TestSkinComponent : ISkinComponent
{
private readonly string name;
public TestSkinComponent(string name)
{
this.name = name;
}
public string ComponentGroup => string.Empty;
public string LookupName => name;
}
}
}

View File

@ -2,6 +2,7 @@
// 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.Audio.Track;
@ -25,6 +26,11 @@ namespace osu.Game.Tests.Visual.UserInterface
{
private readonly NowPlayingOverlay np;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(BeatSyncedContainer)
};
[Cached]
private MusicController musicController = new MusicController();
@ -154,7 +160,9 @@ namespace osu.Game.Tests.Visual.UserInterface
if (timingPoints[timingPoints.Count - 1] == current)
return current;
return timingPoints[timingPoints.IndexOf(current) + 1];
int index = timingPoints.IndexOf(current); // -1 means that this is a "default beat"
return index == -1 ? current : timingPoints[index + 1];
}
private int calculateBeatCount(TimingControlPoint current)

View File

@ -33,23 +33,46 @@ namespace osu.Game.Graphics.Containers
/// </summary>
public double TimeSinceLastBeat { get; private set; }
/// <summary>
/// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing.
/// </summary>
private const double default_beat_length = 60000.0 / 60.0;
private TimingControlPoint defaultTiming;
private EffectControlPoint defaultEffect;
private TrackAmplitudes defaultAmplitudes;
protected override void Update()
{
if (!Beatmap.Value.TrackLoaded || !Beatmap.Value.BeatmapLoaded) return;
Track track = null;
IBeatmap beatmap = null;
var track = Beatmap.Value.Track;
var beatmap = Beatmap.Value.Beatmap;
double currentTrackTime;
TimingControlPoint timingPoint;
EffectControlPoint effectPoint;
if (track == null || beatmap == null)
return;
if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded)
{
track = Beatmap.Value.Track;
beatmap = Beatmap.Value.Beatmap;
}
double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime;
if (track != null && beatmap != null && track.IsRunning)
{
currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
if (timingPoint.BeatLength == 0)
return;
if (timingPoint.BeatLength == 0)
return;
}
else
{
currentTrackTime = Clock.CurrentTime;
timingPoint = defaultTiming;
effectPoint = defaultEffect;
}
int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength);
@ -67,7 +90,7 @@ namespace osu.Game.Graphics.Containers
return;
using (BeginDelayedSequence(-TimeSinceLastBeat, true))
OnNewBeat(beatIndex, timingPoint, effectPoint, track.CurrentAmplitudes);
OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? defaultAmplitudes);
lastBeat = beatIndex;
lastTimingPoint = timingPoint;
@ -77,6 +100,28 @@ namespace osu.Game.Graphics.Containers
private void load(IBindable<WorkingBeatmap> beatmap)
{
Beatmap.BindTo(beatmap);
defaultTiming = new TimingControlPoint
{
BeatLength = default_beat_length,
AutoGenerated = true,
Time = 0
};
defaultEffect = new EffectControlPoint
{
Time = 0,
AutoGenerated = true,
KiaiMode = false,
OmitFirstBarLine = false
};
defaultAmplitudes = new TrackAmplitudes
{
FrequencyAmplitudes = new float[256],
LeftChannel = 0,
RightChannel = 0
};
}
protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)

View File

@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Direct
private BeatmapSetOverlay beatmapSetOverlay;
public PreviewTrack Preview => PlayButton.Preview;
public Bindable<bool> PreviewPlaying => PlayButton.Playing;
public Bindable<bool> PreviewPlaying => PlayButton?.Playing;
protected abstract PlayButton PlayButton { get; }
protected abstract Box PreviewBar { get; }

View File

@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Judgements
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Child = new SkinnableDrawable($"Play/{Result.Type}", _ => JudgementText = new OsuSpriteText
Child = new SkinnableDrawable(new GameplaySkinComponent<HitResult>(Result.Type), _ => JudgementText = new OsuSpriteText
{
Text = Result.Type.GetDescription().ToUpperInvariant(),
Font = OsuFont.Numeric.With(size: 12),

View File

@ -17,7 +17,7 @@ namespace osu.Game.Skinning
private readonly Bindable<bool> beatmapHitsounds = new Bindable<bool>();
protected override bool AllowConfigurationLookup => beatmapSkins.Value;
protected override bool AllowDrawableLookup(string componentName) => beatmapSkins.Value;
protected override bool AllowDrawableLookup(ISkinComponent component) => beatmapSkins.Value;
protected override bool AllowTextureLookup(string componentName) => beatmapSkins.Value;
protected override bool AllowSampleLookup(ISampleInfo componentName) => beatmapHitsounds.Value;

View File

@ -1,4 +1,4 @@
// 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.
using osu.Framework.Audio.Sample;
@ -16,7 +16,7 @@ namespace osu.Game.Skinning
Configuration = new DefaultSkinConfiguration();
}
public override Drawable GetDrawableComponent(string componentName) => null;
public override Drawable GetDrawableComponent(ISkinComponent component) => null;
public override Texture GetTexture(string componentName) => null;

View File

@ -0,0 +1,23 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
namespace osu.Game.Skinning
{
public class GameplaySkinComponent<T> : ISkinComponent where T : struct
{
public readonly T Component;
public GameplaySkinComponent(T component)
{
Component = component;
}
protected virtual string RulesetPrefix => string.Empty;
protected virtual string ComponentName => Component.ToString();
public string LookupName =>
string.Join("/", new[] { "Gameplay", RulesetPrefix, ComponentName }.Where(s => !string.IsNullOrEmpty(s)));
}
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Skinning
/// </summary>
public interface ISkin
{
Drawable GetDrawableComponent(string componentName);
Drawable GetDrawableComponent(ISkinComponent component);
Texture GetTexture(string componentName);

View File

@ -0,0 +1,10 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Skinning
{
public interface ISkinComponent
{
string LookupName { get; }
}
}

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Game.Audio;
using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
namespace osu.Game.Skinning
@ -47,39 +48,30 @@ namespace osu.Game.Skinning
Samples?.Dispose();
}
public override Drawable GetDrawableComponent(string componentName)
public override Drawable GetDrawableComponent(ISkinComponent component)
{
bool animatable = false;
bool looping = true;
switch (componentName)
switch (component)
{
case "Play/Miss":
componentName = "hit0";
animatable = true;
looping = false;
break;
case GameplaySkinComponent<HitResult> resultComponent:
switch (resultComponent.Component)
{
case HitResult.Miss:
return this.GetAnimation("hit0", true, false);
case "Play/Meh":
componentName = "hit50";
animatable = true;
looping = false;
break;
case HitResult.Meh:
return this.GetAnimation("hit50", true, false);
case "Play/Good":
componentName = "hit100";
animatable = true;
looping = false;
break;
case HitResult.Good:
return this.GetAnimation("hit100", true, false);
case HitResult.Great:
return this.GetAnimation("hit300", true, false);
}
case "Play/Great":
componentName = "hit300";
animatable = true;
looping = false;
break;
}
return this.GetAnimation(componentName, animatable, looping);
return this.GetAnimation(component.LookupName, false, false);
}
public override Texture GetTexture(string componentName)

View File

@ -1,4 +1,4 @@
// 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.
using System;
@ -15,7 +15,7 @@ namespace osu.Game.Skinning
public virtual SkinConfiguration Configuration { get; protected set; }
public abstract Drawable GetDrawableComponent(string componentName);
public abstract Drawable GetDrawableComponent(ISkinComponent componentName);
public abstract SampleChannel GetSample(ISampleInfo sampleInfo);

View File

@ -1,4 +1,4 @@
// 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.
using System;
@ -125,7 +125,7 @@ namespace osu.Game.Skinning
public event Action SourceChanged;
public Drawable GetDrawableComponent(string componentName) => CurrentSkin.Value.GetDrawableComponent(componentName);
public Drawable GetDrawableComponent(ISkinComponent component) => CurrentSkin.Value.GetDrawableComponent(component);
public Texture GetTexture(string componentName) => CurrentSkin.Value.GetTexture(componentName);

View File

@ -22,7 +22,7 @@ namespace osu.Game.Skinning
private ISkinSource fallbackSource;
protected virtual bool AllowDrawableLookup(string componentName) => true;
protected virtual bool AllowDrawableLookup(ISkinComponent component) => true;
protected virtual bool AllowTextureLookup(string componentName) => true;
@ -37,13 +37,13 @@ namespace osu.Game.Skinning
RelativeSizeAxes = Axes.Both;
}
public Drawable GetDrawableComponent(string componentName)
public Drawable GetDrawableComponent(ISkinComponent component)
{
Drawable sourceDrawable;
if (AllowDrawableLookup(componentName) && (sourceDrawable = skin?.GetDrawableComponent(componentName)) != null)
if (AllowDrawableLookup(component) && (sourceDrawable = skin?.GetDrawableComponent(component)) != null)
return sourceDrawable;
return fallbackSource?.GetDrawableComponent(componentName);
return fallbackSource?.GetDrawableComponent(component);
}
public Texture GetTexture(string componentName)

View File

@ -18,39 +18,39 @@ namespace osu.Game.Skinning
/// </summary>
public Drawable Drawable { get; private set; }
private readonly string componentName;
private readonly ISkinComponent component;
private readonly ConfineMode confineMode;
/// <summary>
/// Create a new skinnable drawable.
/// </summary>
/// <param name="name">The namespace-complete resource name for this skinnable element.</param>
/// <param name="component">The namespace-complete resource name for this skinnable element.</param>
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
/// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
/// <param name="confineMode">How (if at all) the <see cref="Drawable"/> should be resize to fit within our own bounds.</param>
public SkinnableDrawable(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: this(name, allowFallback, confineMode)
public SkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: this(component, allowFallback, confineMode)
{
createDefault = defaultImplementation;
}
protected SkinnableDrawable(string name, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
protected SkinnableDrawable(ISkinComponent component, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(allowFallback)
{
componentName = name;
this.component = component;
this.confineMode = confineMode;
RelativeSizeAxes = Axes.Both;
}
private readonly Func<string, Drawable> createDefault;
private readonly Func<ISkinComponent, Drawable> createDefault;
private readonly Cached scaling = new Cached();
private bool isDefault;
protected virtual Drawable CreateDefault(string name) => createDefault(name);
protected virtual Drawable CreateDefault(ISkinComponent component) => createDefault(component);
/// <summary>
/// Whether to apply size restrictions (specified via <see cref="confineMode"/>) to the default implementation.
@ -59,13 +59,13 @@ namespace osu.Game.Skinning
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
Drawable = skin.GetDrawableComponent(componentName);
Drawable = skin.GetDrawableComponent(component);
isDefault = false;
if (Drawable == null && allowFallback)
{
Drawable = CreateDefault(componentName);
Drawable = CreateDefault(component);
isDefault = true;
}

View File

@ -6,6 +6,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Audio;
@ -14,6 +15,9 @@ namespace osu.Game.Skinning
public class SkinnableSound : SkinReloadableDrawable
{
private readonly ISampleInfo[] hitSamples;
private List<(AdjustableProperty property, BindableDouble bindable)> adjustments;
private SampleChannel[] channels;
private AudioManager audio;
@ -34,8 +38,39 @@ namespace osu.Game.Skinning
this.audio = audio;
}
private bool looping;
public bool Looping
{
get => looping;
set
{
if (value == looping) return;
looping = value;
channels?.ForEach(c => c.Looping = looping);
}
}
public void Play() => channels?.ForEach(c => c.Play());
public void Stop() => channels?.ForEach(c => c.Stop());
public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable)
{
if (adjustments == null) adjustments = new List<(AdjustableProperty, BindableDouble)>();
adjustments.Add((type, adjustBindable));
channels?.ForEach(c => c.AddAdjustment(type, adjustBindable));
}
public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable)
{
adjustments?.Remove((type, adjustBindable));
channels?.ForEach(c => c.RemoveAdjustment(type, adjustBindable));
}
public override bool IsPresent => Scheduler.HasPendingTasks;
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
@ -50,8 +85,15 @@ namespace osu.Game.Skinning
break;
if (ch != null)
{
ch.Looping = looping;
ch.Volume.Value = s.Volume / 100.0;
if (adjustments != null)
foreach (var adjustment in adjustments)
ch.AddAdjustment(adjustment.property, adjustment.bindable);
}
return ch;
}).Where(c => c != null).ToArray();
}

View File

@ -19,11 +19,11 @@ namespace osu.Game.Skinning
[Resolved]
private TextureStore textures { get; set; }
public SkinnableSprite(string name, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(name, allowFallback, confineMode)
public SkinnableSprite(ISkinComponent component, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(component, allowFallback, confineMode)
{
}
protected override Drawable CreateDefault(string name) => new Sprite { Texture = textures.Get(name) };
protected override Drawable CreateDefault(ISkinComponent component) => new Sprite { Texture = textures.Get(component.LookupName) };
}
}

View File

@ -8,8 +8,8 @@ namespace osu.Game.Skinning
{
public class SkinnableSpriteText : SkinnableDrawable, IHasText
{
public SkinnableSpriteText(string name, Func<string, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(name, defaultImplementation, allowFallback, confineMode)
public SkinnableSpriteText(ISkinComponent component, Func<ISkinComponent, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(component, defaultImplementation, allowFallback, confineMode)
{
}

View File

@ -14,7 +14,7 @@
<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.2" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.830.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.903.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.830.1" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />

View File

@ -117,7 +117,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.830.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.903.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.830.1" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.830.1" />
<PackageReference Include="SharpCompress" Version="0.24.0" />