1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-15 17:33:02 +08:00

Merge remote-tracking branch 'refs/remotes/ppy/master' into beatmap-mod-selector

This commit is contained in:
Andrei Zavatski 2019-09-04 02:46:23 +03:00
commit 04111cc3b7
90 changed files with 564 additions and 171 deletions

View File

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

View File

@ -82,11 +82,11 @@ namespace osu.Game.Rulesets.Catch.Tests
remove { } 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(); 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 IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(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[] public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{ {
new KeyBinding(InputKey.Z, CatchAction.MoveLeft), new KeyBinding(InputKey.Z, CatchAction.MoveLeft),
@ -117,7 +119,7 @@ namespace osu.Game.Rulesets.Catch
public override string Description => "osu!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 }; 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 => "catch"; // todo: use CatchRuleset.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.Catch
{
public enum CatchSkinComponents
{
}
}

View File

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

View File

@ -11,7 +11,9 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing; using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
using osu.Game.Rulesets.Mania.Difficulty.Skills; using osu.Game.Rulesets.Mania.Difficulty.Skills;
using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Difficulty namespace osu.Game.Rulesets.Mania.Difficulty
{ {
@ -32,12 +34,15 @@ namespace osu.Game.Rulesets.Mania.Difficulty
if (beatmap.HitObjects.Count == 0) if (beatmap.HitObjects.Count == 0)
return new ManiaDifficultyAttributes { Mods = mods, Skills = skills }; return new ManiaDifficultyAttributes { Mods = mods, Skills = skills };
HitWindows hitWindows = new ManiaHitWindows();
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
return new ManiaDifficultyAttributes return new ManiaDifficultyAttributes
{ {
StarRating = difficultyValue(skills) * star_scaling_factor, StarRating = difficultyValue(skills) * star_scaling_factor,
Mods = mods, Mods = mods,
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate, GreatHitWindow = (int)(hitWindows.Great / 2) / clockRate,
Skills = skills Skills = skills
}; };
} }

View File

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

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
@ -209,6 +210,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
Debug.Assert(HitObject.HitWindows != null);
// Factor in the release lenience // Factor in the release lenience
timeOffset /= release_window_lenience; timeOffset /= release_window_lenience;

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -52,6 +53,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
Debug.Assert(HitObject.HitWindows != null);
if (!userTriggered) if (!userTriggered)
{ {
if (!HitObject.HitWindows.CanBeHit(timeOffset)) if (!HitObject.HitWindows.CanBeHit(timeOffset))

View File

@ -5,6 +5,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Mania.Objects namespace osu.Game.Rulesets.Mania.Objects
@ -99,5 +100,7 @@ namespace osu.Game.Rulesets.Mania.Objects
} }
public override Judgement CreateJudgement() => new HoldNoteJudgement(); public override Judgement CreateJudgement() => new HoldNoteJudgement();
protected override HitWindows CreateHitWindows() => null;
} }
} }

View File

@ -3,6 +3,7 @@
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Objects namespace osu.Game.Rulesets.Mania.Objects
{ {
@ -12,5 +13,7 @@ namespace osu.Game.Rulesets.Mania.Objects
public class HoldNoteTick : ManiaHitObject public class HoldNoteTick : ManiaHitObject
{ {
public override Judgement CreateJudgement() => new HoldNoteTickJudgement(); public override Judgement CreateJudgement() => new HoldNoteTickJudgement();
protected override HitWindows CreateHitWindows() => null;
} }
} }

View File

@ -7,6 +7,7 @@ using System.Linq;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -24,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1)) foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
AddStep("Show " + result.GetDescription(), () => SetContents(() => AddStep("Show " + result.GetDescription(), () => SetContents(() =>
new DrawableOsuJudgement(new JudgementResult(null) { Type = result }, null) new DrawableOsuJudgement(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -13,8 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
var drawableHitObject = base.CreateDrawableHitCircle(circle, auto); var drawableHitObject = base.CreateDrawableHitCircle(circle, auto);
Scheduler.AddDelayed(() => drawableHitObject.TriggerJudgement(), Debug.Assert(drawableHitObject.HitObject.HitWindows != null);
drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current);
double delay = drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current;
Scheduler.AddDelayed(() => drawableHitObject.TriggerJudgement(), delay);
return drawableHitObject; return drawableHitObject;
} }

View File

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

View File

@ -9,6 +9,7 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Difficulty.Skills; using osu.Game.Rulesets.Osu.Difficulty.Skills;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
@ -34,8 +35,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
HitWindows hitWindows = new OsuHitWindows();
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate; double hitWindowGreat = (int)(hitWindows.Great / 2) / clockRate;
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
int maxCombo = beatmap.HitObjects.Count; int maxCombo = beatmap.HitObjects.Count;

View File

@ -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.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Osu.Judgements namespace osu.Game.Rulesets.Osu.Judgements
{ {
@ -9,8 +10,8 @@ namespace osu.Game.Rulesets.Osu.Judgements
{ {
public ComboResult ComboType; public ComboResult ComboType;
public OsuJudgementResult(Judgement judgement) public OsuJudgementResult(HitObject hitObject, Judgement judgement)
: base(judgement) : base(hitObject, judgement)
{ {
} }
} }

View File

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

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -38,7 +39,12 @@ namespace osu.Game.Rulesets.Osu.Mods
if ((osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime) || osuHit.IsHit) if ((osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime) || osuHit.IsHit)
continue; continue;
requiresHit |= osuHit is DrawableHitCircle && osuHit.IsHovered && osuHit.HitObject.HitWindows.CanBeHit(relativetime); if (osuHit is DrawableHitCircle && osuHit.IsHovered)
{
Debug.Assert(osuHit.HitObject.HitWindows != null);
requiresHit |= osuHit.HitObject.HitWindows.CanBeHit(relativetime);
}
requiresHold |= (osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered)) || osuHit is DrawableSpinner; requiresHold |= (osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered)) || osuHit is DrawableSpinner;
} }

View File

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

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Diagnostics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return true; 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 ApproachCircle = new ApproachCircle
{ {
Alpha = 0, Alpha = 0,
@ -87,6 +88,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
Debug.Assert(HitObject.HitWindows != null);
if (!userTriggered) if (!userTriggered)
{ {
if (!HitObject.HitWindows.CanBeHit(timeOffset)) if (!HitObject.HitWindows.CanBeHit(timeOffset))
@ -119,6 +122,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)
{ {
Debug.Assert(HitObject.HitWindows != null);
switch (state) switch (state)
{ {
case ArmedState.Idle: case ArmedState.Idle:

View File

@ -41,6 +41,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength); protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement); protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
} }
} }

View File

@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Blending = BlendingParameters.Additive; Blending = BlendingParameters.Additive;
Origin = Anchor.Centre; 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, RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.Solid.ChevronRight, Icon = FontAwesome.Solid.ChevronRight,

View File

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

View File

@ -31,13 +31,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private class SkinnableApproachCircle : SkinnableSprite private class SkinnableApproachCircle : SkinnableSprite
{ {
public SkinnableApproachCircle() public SkinnableApproachCircle()
: base("Play/osu/approachcircle") : base("Gameplay/osu/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, // 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. // 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, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Texture = textures.Get(@"Play/osu/disc"), Texture = textures.Get(@"Gameplay/osu/disc"),
}, },
new TrianglesPiece new TrianglesPiece
{ {

View File

@ -3,7 +3,6 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Skinning;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@ -20,12 +19,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Blending = BlendingParameters.Additive; Blending = BlendingParameters.Additive;
Alpha = 0; Alpha = 0;
Child = new SkinnableDrawable("Play/osu/hitcircle-explode", _ => new TrianglesPiece Child = new TrianglesPiece
{ {
Blending = BlendingParameters.Additive, Blending = BlendingParameters.Additive,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Alpha = 0.2f, 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 osu.Framework.Graphics.Containers;
using osuTK; using osuTK;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
@ -21,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Blending = BlendingParameters.Additive; Blending = BlendingParameters.Additive;
Alpha = 0; Alpha = 0;
Child = new SkinnableDrawable("Play/osu/hitcircle-flash", name => new CircularContainer Child = new CircularContainer
{ {
Masking = true, Masking = true,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -29,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
RelativeSizeAxes = Axes.Both 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.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
@ -22,14 +21,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(TextureStore textures) private void load(TextureStore textures)
{ {
Child = new SkinnableDrawable("Play/osu/ring-glow", name => new Sprite Child = new Sprite
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Texture = textures.Get(name), Texture = textures.Get("Gameplay/osu/ring-glow"),
Blending = BlendingParameters.Additive, Blending = BlendingParameters.Additive,
Alpha = 0.5f 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[] Children = new Drawable[]
{ {
new SkinnableDrawable("Play/osu/number-glow", name => new CircularContainer new CircularContainer
{ {
Masking = true, Masking = true,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -41,8 +41,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Colour = Color4.White.Opacity(0.5f), Colour = Color4.White.Opacity(0.5f),
}, },
Child = new Box() 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), Font = OsuFont.Numeric.With(size: 40),
UseFullGlyphHeight = false, UseFullGlyphHeight = false,

View File

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

View File

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

View File

@ -229,5 +229,7 @@ namespace osu.Game.Rulesets.Osu.Objects
nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples; nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples;
public override Judgement CreateJudgement() => new OsuJudgement(); public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => null;
} }
} }

View File

@ -4,6 +4,7 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects namespace osu.Game.Rulesets.Osu.Objects
@ -30,5 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects
} }
public override Judgement CreateJudgement() => new OsuJudgement(); public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => null;
} }
} }

View File

@ -6,6 +6,7 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects namespace osu.Game.Rulesets.Osu.Objects
@ -31,5 +32,7 @@ namespace osu.Game.Rulesets.Osu.Objects
} }
public override Judgement CreateJudgement() => new OsuJudgement(); public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => null;
} }
} }

View File

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

@ -6,9 +6,11 @@ using osu.Framework.MathUtils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Replays; using osu.Game.Replays;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -36,6 +38,8 @@ namespace osu.Game.Rulesets.Osu.Replays
/// </summary> /// </summary>
private readonly double reactionTime; private readonly double reactionTime;
private readonly HitWindows defaultHitWindows;
/// <summary> /// <summary>
/// What easing to use when moving between hitobjects /// What easing to use when moving between hitobjects
/// </summary> /// </summary>
@ -50,6 +54,9 @@ namespace osu.Game.Rulesets.Osu.Replays
{ {
// Already superhuman, but still somewhat realistic // Already superhuman, but still somewhat realistic
reactionTime = ApplyModsToRate(100); reactionTime = ApplyModsToRate(100);
defaultHitWindows = new OsuHitWindows();
defaultHitWindows.SetDifficulty(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
} }
#endregion #endregion
@ -91,21 +98,49 @@ namespace osu.Game.Rulesets.Osu.Replays
{ {
double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime; double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime;
HitWindows hitWindows = null;
switch (h)
{
case HitCircle hitCircle:
hitWindows = hitCircle.HitWindows;
break;
case Slider slider:
hitWindows = slider.TailCircle.HitWindows;
break;
case Spinner _:
hitWindows = defaultHitWindows;
break;
}
Debug.Assert(hitWindows != null);
// Make the cursor stay at a hitObject as long as possible (mainly for autopilot). // Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Miss) > endTime + hitWindows.HalfWindowFor(HitResult.Meh) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(prev is Spinner) && h.StartTime - endTime < 1000)
if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
if (!(h is Spinner))
AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
} }
else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) else if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Meh) > endTime + hitWindows.HalfWindowFor(HitResult.Meh) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(prev is Spinner) && h.StartTime - endTime < 1000)
if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
if (!(h is Spinner))
AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
} }
else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good) > endTime + h.HitWindows.HalfWindowFor(HitResult.Good) + 50) else if (h.StartTime - hitWindows.HalfWindowFor(HitResult.Good) > endTime + hitWindows.HalfWindowFor(HitResult.Good) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(prev is Spinner) && h.StartTime - endTime < 1000)
if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); AddFrameToReplay(new OsuReplayFrame(endTime + hitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
if (!(h is Spinner))
AddFrameToReplay(new OsuReplayFrame(h.StartTime - hitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
} }
} }

View File

@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
} }
} }
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement); protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement);
public override HitWindows CreateHitWindows() => new OsuHitWindows(); public override HitWindows CreateHitWindows() => new OsuHitWindows();
} }

View File

@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = 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), Font = OsuFont.Numeric.With(size: 40),
UseFullGlyphHeight = false, UseFullGlyphHeight = false,

View File

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

View File

@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = 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, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,

View File

@ -144,7 +144,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement()) { Type = hitResult }); ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
} }
private void addStrongHitJudgement(bool kiai) private void addStrongHitJudgement(bool kiai)
@ -159,13 +159,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement()) { Type = hitResult }); ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new TaikoStrongJudgement()) { Type = HitResult.Great }); ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great });
} }
private void addMissJudgement() private void addMissJudgement()
{ {
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new DrawableTestHit(new Hit()), new JudgementResult(new TaikoJudgement()) { Type = HitResult.Miss }); ((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new DrawableTestHit(new Hit()), new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = HitResult.Miss });
} }
private void addBarLine(bool major, double delay = scroll_time) private void addBarLine(bool major, double delay = scroll_time)

View File

@ -8,6 +8,7 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
using osu.Game.Rulesets.Taiko.Difficulty.Skills; using osu.Game.Rulesets.Taiko.Difficulty.Skills;
using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Mods;
@ -29,12 +30,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (beatmap.HitObjects.Count == 0) if (beatmap.HitObjects.Count == 0)
return new TaikoDifficultyAttributes { Mods = mods, Skills = skills }; return new TaikoDifficultyAttributes { Mods = mods, Skills = skills };
HitWindows hitWindows = new TaikoHitWindows();
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
return new TaikoDifficultyAttributes return new TaikoDifficultyAttributes
{ {
StarRating = skills.Single().DifficultyValue() * star_scaling_factor, StarRating = skills.Single().DifficultyValue() * star_scaling_factor,
Mods = mods, Mods = mods,
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate, GreatHitWindow = (int)(hitWindows.Great / 2) / clockRate,
MaxCombo = beatmap.HitObjects.Count(h => h is Hit), MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
Skills = skills Skills = skills
}; };

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
@ -34,6 +35,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
Debug.Assert(HitObject.HitWindows != null);
if (!userTriggered) if (!userTriggered)
{ {
if (!HitObject.HitWindows.CanBeHit(timeOffset)) if (!HitObject.HitWindows.CanBeHit(timeOffset))
@ -94,6 +97,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)
{ {
Debug.Assert(HitObject.HitWindows != null);
switch (state) switch (state)
{ {
case ArmedState.Idle: case ArmedState.Idle:

View File

@ -6,6 +6,7 @@ using System;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects namespace osu.Game.Rulesets.Taiko.Objects
@ -86,5 +87,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
} }
public override Judgement CreateJudgement() => new TaikoDrumRollJudgement(); public override Judgement CreateJudgement() => new TaikoDrumRollJudgement();
protected override HitWindows CreateHitWindows() => null;
} }
} }

View File

@ -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.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects namespace osu.Game.Rulesets.Taiko.Objects
@ -25,5 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
public double HitWindow => TickSpacing / 2; public double HitWindow => TickSpacing / 2;
public override Judgement CreateJudgement() => new TaikoDrumRollTickJudgement(); public override Judgement CreateJudgement() => new TaikoDrumRollTickJudgement();
protected override HitWindows CreateHitWindows() => null;
} }
} }

View File

@ -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.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects namespace osu.Game.Rulesets.Taiko.Objects
@ -9,5 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
public class StrongHitObject : TaikoHitObject public class StrongHitObject : TaikoHitObject
{ {
public override Judgement CreateJudgement() => new TaikoStrongJudgement(); public override Judgement CreateJudgement() => new TaikoStrongJudgement();
protected override HitWindows CreateHitWindows() => null;
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects namespace osu.Game.Rulesets.Taiko.Objects
@ -33,5 +34,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
} }
public override Judgement CreateJudgement() => new TaikoSwellJudgement(); public override Judgement CreateJudgement() => new TaikoSwellJudgement();
protected override HitWindows CreateHitWindows() => null;
} }
} }

View File

@ -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.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects namespace osu.Game.Rulesets.Taiko.Objects
@ -9,5 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
public class SwellTick : TaikoHitObject public class SwellTick : TaikoHitObject
{ {
public override Judgement CreateJudgement() => new TaikoSwellTickJudgement(); public override Judgement CreateJudgement() => new TaikoSwellTickJudgement();
protected override HitWindows CreateHitWindows() => null;
} }
} }

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 DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableTaikoRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
public const string SHORT_NAME = "taiko";
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[] public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{ {
new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre),
@ -116,7 +118,7 @@ namespace osu.Game.Rulesets.Taiko
public override string Description => "osu!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 }; 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] [BackgroundDependencyLoader]
private void load(TextureStore textures, OsuColour colours) private void load(TextureStore textures, OsuColour colours)
{ {
rim.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer"); rim.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-outer");
rimHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer-hit"); rimHit.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-outer-hit");
centre.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner"); centre.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-inner");
centreHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner-hit"); centreHit.Texture = textures.Get(@"Gameplay/Taiko/taiko-drum-inner-hit");
rimHit.Colour = colours.Blue; rimHit.Colour = colours.Blue;
centreHit.Colour = colours.Pink; centreHit.Colour = colours.Pink;

View File

@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private void newJudgement(double offset = 0) private void newJudgement(double offset = 0)
{ {
var judgement = new JudgementResult(new Judgement()) var judgement = new JudgementResult(new HitObject(), new Judgement())
{ {
TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset, TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset,
Type = HitResult.Perfect, Type = HitResult.Perfect,

View File

@ -137,8 +137,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
public new Drawable Drawable => base.Drawable; public new Drawable Drawable => base.Drawable;
public ExposedSkinnableDrawable(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) public ExposedSkinnableDrawable(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(name, defaultImplementation, allowFallback, confineMode) : base(new TestSkinComponent(name), defaultImplementation, allowFallback, confineMode)
{ {
} }
} }
@ -206,8 +206,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public new Drawable Drawable => base.Drawable; public new Drawable Drawable => base.Drawable;
public int SkinChangedCount { get; private set; } public int SkinChangedCount { get; private set; }
public SkinConsumer(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null) public SkinConsumer(string name, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null)
: base(name, defaultImplementation, allowFallback) : base(new TestSkinComponent(name), defaultImplementation, allowFallback)
{ {
} }
@ -243,8 +243,8 @@ namespace osu.Game.Tests.Visual.Gameplay
this.size = size; this.size = size;
} }
public Drawable GetDrawableComponent(string componentName) => public Drawable GetDrawableComponent(ISkinComponent componentName) =>
componentName == "available" componentName.LookupName == "available"
? new DrawWidthBox ? new DrawWidthBox
{ {
Colour = Color4.Yellow, Colour = Color4.Yellow,
@ -261,7 +261,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private class SecondarySource : ISkin 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(); public Texture GetTexture(string componentName) => throw new NotImplementedException();
@ -272,7 +272,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private class SkinSourceContainer : Container, ISkin 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(); 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(); 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. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
@ -25,6 +26,11 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
private readonly NowPlayingOverlay np; private readonly NowPlayingOverlay np;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(BeatSyncedContainer)
};
[Cached] [Cached]
private MusicController musicController = new MusicController(); private MusicController musicController = new MusicController();
@ -154,7 +160,9 @@ namespace osu.Game.Tests.Visual.UserInterface
if (timingPoints[timingPoints.Count - 1] == current) if (timingPoints[timingPoints.Count - 1] == current)
return 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) private int calculateBeatCount(TimingControlPoint current)

View File

@ -33,23 +33,46 @@ namespace osu.Game.Graphics.Containers
/// </summary> /// </summary>
public double TimeSinceLastBeat { get; private set; } 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() protected override void Update()
{ {
if (!Beatmap.Value.TrackLoaded || !Beatmap.Value.BeatmapLoaded) return; Track track = null;
IBeatmap beatmap = null;
var track = Beatmap.Value.Track; double currentTrackTime;
var beatmap = Beatmap.Value.Beatmap; TimingControlPoint timingPoint;
EffectControlPoint effectPoint;
if (track == null || beatmap == null) if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded)
return; {
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); timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
if (timingPoint.BeatLength == 0) if (timingPoint.BeatLength == 0)
return; return;
}
else
{
currentTrackTime = Clock.CurrentTime;
timingPoint = defaultTiming;
effectPoint = defaultEffect;
}
int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength); int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength);
@ -67,7 +90,7 @@ namespace osu.Game.Graphics.Containers
return; return;
using (BeginDelayedSequence(-TimeSinceLastBeat, true)) using (BeginDelayedSequence(-TimeSinceLastBeat, true))
OnNewBeat(beatIndex, timingPoint, effectPoint, track.CurrentAmplitudes); OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? defaultAmplitudes);
lastBeat = beatIndex; lastBeat = beatIndex;
lastTimingPoint = timingPoint; lastTimingPoint = timingPoint;
@ -77,6 +100,28 @@ namespace osu.Game.Graphics.Containers
private void load(IBindable<WorkingBeatmap> beatmap) private void load(IBindable<WorkingBeatmap> beatmap)
{ {
Beatmap.BindTo(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) protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)

View File

@ -1,11 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osuTK.Input;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
{ {
@ -16,16 +18,28 @@ namespace osu.Game.Graphics.UserInterface
public class HoverClickSounds : HoverSounds public class HoverClickSounds : HoverSounds
{ {
private SampleChannel sampleClick; private SampleChannel sampleClick;
private readonly MouseButton[] buttons;
public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) /// <summary>
/// a container which plays sounds on hover and click for any specified <see cref="MouseButton"/>s.
/// </summary>
/// <param name="sampleSet">Set of click samples to play.</param>
/// <param name="buttons">
/// Array of button codes which should trigger the click sound.
/// If this optional parameter is omitted or set to <code>null</code>, the click sound will only be played on left click.
/// </param>
public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal, MouseButton[] buttons = null)
: base(sampleSet) : base(sampleSet)
{ {
this.buttons = buttons ?? new[] { MouseButton.Left };
} }
protected override bool OnClick(ClickEvent e) protected override bool OnMouseUp(MouseUpEvent e)
{ {
sampleClick?.Play(); if (buttons.Contains(e.Button) && Contains(e.ScreenSpaceMousePosition))
return base.OnClick(e); sampleClick?.Play();
return base.OnMouseUp(e);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -121,10 +121,11 @@ namespace osu.Game.Overlays.Chat.Selection
{ {
new SpriteIcon new SpriteIcon
{ {
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Icon = FontAwesome.Solid.User, Icon = FontAwesome.Solid.User,
Size = new Vector2(text_size - 2), Size = new Vector2(text_size - 2),
Shadow = false, Shadow = false,
Margin = new MarginPadding { Top = 1 },
}, },
new OsuSpriteText new OsuSpriteText
{ {

View File

@ -151,6 +151,7 @@ namespace osu.Game.Overlays.Direct
AutoSizeAxes = Axes.X, AutoSizeAxes = Axes.X,
Height = 20, Height = 20,
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
Spacing = new Vector2(3),
Children = GetDifficultyIcons(colours), Children = GetDifficultyIcons(colours),
}, },
}, },

View File

@ -129,6 +129,7 @@ namespace osu.Game.Overlays.Direct
AutoSizeAxes = Axes.X, AutoSizeAxes = Axes.X,
Height = 20, Height = 20,
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
Spacing = new Vector2(3),
Children = GetDifficultyIcons(colours), Children = GetDifficultyIcons(colours),
}, },
}, },

View File

@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Direct
public readonly BeatmapSetInfo SetInfo; public readonly BeatmapSetInfo SetInfo;
private const double hover_transition_time = 400; private const double hover_transition_time = 400;
private const int maximum_difficulty_icons = 15; private const int maximum_difficulty_icons = 10;
private Container content; private Container content;
@ -190,10 +190,11 @@ namespace osu.Game.Overlays.Direct
text = new OsuSpriteText { Font = OsuFont.GetFont(weight: FontWeight.SemiBold, italics: true) }, text = new OsuSpriteText { Font = OsuFont.GetFont(weight: FontWeight.SemiBold, italics: true) },
new SpriteIcon new SpriteIcon
{ {
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Icon = icon, Icon = icon,
Shadow = true, Shadow = true,
Size = new Vector2(14), Size = new Vector2(14),
Margin = new MarginPadding { Top = 1 },
}, },
}; };

View File

@ -283,7 +283,7 @@ namespace osu.Game.Overlays.Mods
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Font = OsuFont.GetFont(size: 18) Font = OsuFont.GetFont(size: 18)
}, },
new HoverClickSounds() new HoverClickSounds(buttons: new[] { MouseButton.Left, MouseButton.Right })
}; };
Mod = mod; Mod = mod;

View File

@ -161,12 +161,12 @@ namespace osu.Game.Overlays.Music
{ {
public PlaylistItemHandle() public PlaylistItemHandle()
{ {
Anchor = Anchor.TopLeft; Anchor = Anchor.CentreLeft;
Origin = Anchor.TopLeft; Origin = Anchor.CentreLeft;
Size = new Vector2(12); Size = new Vector2(12);
Icon = FontAwesome.Solid.Bars; Icon = FontAwesome.Solid.Bars;
Alpha = 0f; Alpha = 0f;
Margin = new MarginPadding { Left = 5, Top = 2 }; Margin = new MarginPadding { Left = 5 };
} }
public override bool HandlePositionalInput => IsPresent; public override bool HandlePositionalInput => IsPresent;

View File

@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Judgements
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both, 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(), Text = Result.Type.GetDescription().ToUpperInvariant(),
Font = OsuFont.Numeric.With(size: 12), Font = OsuFont.Numeric.With(size: 12),

View File

@ -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 JetBrains.Annotations;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -16,9 +18,16 @@ namespace osu.Game.Rulesets.Judgements
/// </summary> /// </summary>
public HitResult Type; public HitResult Type;
/// <summary>
/// The <see cref="HitObject"/> which was judged.
/// </summary>
[NotNull]
public readonly HitObject HitObject;
/// <summary> /// <summary>
/// The <see cref="Judgement"/> which this <see cref="JudgementResult"/> applies for. /// The <see cref="Judgement"/> which this <see cref="JudgementResult"/> applies for.
/// </summary> /// </summary>
[NotNull]
public readonly Judgement Judgement; public readonly Judgement Judgement;
/// <summary> /// <summary>
@ -55,9 +64,11 @@ namespace osu.Game.Rulesets.Judgements
/// <summary> /// <summary>
/// Creates a new <see cref="JudgementResult"/>. /// Creates a new <see cref="JudgementResult"/>.
/// </summary> /// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> which was judged.</param>
/// <param name="judgement">The <see cref="Judgement"/> to refer to for scoring information.</param> /// <param name="judgement">The <see cref="Judgement"/> to refer to for scoring information.</param>
public JudgementResult(Judgement judgement) public JudgementResult([NotNull] HitObject hitObject, [NotNull] Judgement judgement)
{ {
HitObject = hitObject;
Judgement = judgement; Judgement = judgement;
} }

View File

@ -410,7 +410,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// Creates the <see cref="JudgementResult"/> that represents the scoring result for this <see cref="DrawableHitObject"/>. /// Creates the <see cref="JudgementResult"/> that represents the scoring result for this <see cref="DrawableHitObject"/>.
/// </summary> /// </summary>
/// <param name="judgement">The <see cref="Judgement"/> that provides the scoring information.</param> /// <param name="judgement">The <see cref="Judgement"/> that provides the scoring information.</param>
protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(judgement); protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(HitObject, judgement);
} }
public abstract class DrawableHitObject<TObject> : DrawableHitObject public abstract class DrawableHitObject<TObject> : DrawableHitObject

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -56,6 +57,7 @@ namespace osu.Game.Rulesets.Objects
/// <summary> /// <summary>
/// The hit windows for this <see cref="HitObject"/>. /// The hit windows for this <see cref="HitObject"/>.
/// </summary> /// </summary>
[CanBeNull]
public HitWindows HitWindows { get; set; } public HitWindows HitWindows { get; set; }
private readonly List<HitObject> nestedHitObjects = new List<HitObject>(); private readonly List<HitObject> nestedHitObjects = new List<HitObject>();
@ -111,11 +113,12 @@ namespace osu.Game.Rulesets.Objects
/// <summary> /// <summary>
/// Creates the <see cref="HitWindows"/> for this <see cref="HitObject"/>. /// Creates the <see cref="HitWindows"/> for this <see cref="HitObject"/>.
/// This can be null to indicate that the <see cref="HitObject"/> has no <see cref="HitWindows"/>. /// This can be null to indicate that the <see cref="HitObject"/> has no <see cref="HitWindows"/> and timing errors should not be displayed to the user.
/// <para> /// <para>
/// This will only be invoked if <see cref="HitWindows"/> hasn't been set externally (e.g. from a <see cref="BeatmapConverter{T}"/>. /// This will only be invoked if <see cref="HitWindows"/> hasn't been set externally (e.g. from a <see cref="BeatmapConverter{T}"/>.
/// </para> /// </para>
/// </summary> /// </summary>
[CanBeNull]
protected virtual HitWindows CreateHitWindows() => new HitWindows(); protected virtual HitWindows CreateHitWindows() => new HitWindows();
} }
} }

View File

@ -275,7 +275,7 @@ namespace osu.Game.Rulesets.Scoring
if (judgement == null) if (judgement == null)
return; return;
var result = CreateResult(judgement); var result = CreateResult(obj, judgement);
if (result == null) if (result == null)
throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
@ -441,8 +441,9 @@ namespace osu.Game.Rulesets.Scoring
/// <summary> /// <summary>
/// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>. /// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>.
/// </summary> /// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> which was judged.</param>
/// <param name="judgement">The <see cref="Judgement"/> that provides the scoring information.</param> /// <param name="judgement">The <see cref="Judgement"/> that provides the scoring information.</param>
protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(judgement); protected virtual JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new JudgementResult(hitObject, judgement);
} }
public enum ScoringMode public enum ScoringMode

View File

@ -13,6 +13,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using JetBrains.Annotations;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Input; using osu.Framework.Input;
@ -390,6 +391,28 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
public ResumeOverlay ResumeOverlay { get; protected set; } public ResumeOverlay ResumeOverlay { get; protected set; }
/// <summary>
/// Returns first available <see cref="HitWindows"/> provided by a <see cref="HitObject"/>.
/// </summary>
[CanBeNull]
public HitWindows FirstAvailableHitWindows
{
get
{
foreach (var h in Objects)
{
if (h.HitWindows != null)
return h.HitWindows;
foreach (var n in h.NestedHitObjects)
if (n.HitWindows != null)
return n.HitWindows;
}
return null;
}
}
protected virtual ResumeOverlay CreateResumeOverlay() => null; protected virtual ResumeOverlay CreateResumeOverlay() => null;
/// <summary> /// <summary>

View File

@ -49,6 +49,9 @@ namespace osu.Game.Screens.Play.HUD
private void onNewJudgement(JudgementResult result) private void onNewJudgement(JudgementResult result)
{ {
if (result.HitObject.HitWindows == null)
return;
foreach (var c in Children) foreach (var c in Children)
c.OnNewJudgement(result); c.OnNewJudgement(result);
} }

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -259,7 +258,7 @@ namespace osu.Game.Screens.Play
Margin = new MarginPadding { Top = 20, Right = 10 }, Margin = new MarginPadding { Top = 20, Right = 10 },
}; };
protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.Objects.FirstOrDefault()?.HitWindows); protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.FirstAvailableHitWindows);
protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay();

View File

@ -17,7 +17,7 @@ namespace osu.Game.Skinning
private readonly Bindable<bool> beatmapHitsounds = new Bindable<bool>(); private readonly Bindable<bool> beatmapHitsounds = new Bindable<bool>();
protected override bool AllowConfigurationLookup => beatmapSkins.Value; 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 AllowTextureLookup(string componentName) => beatmapSkins.Value;
protected override bool AllowSampleLookup(ISampleInfo componentName) => beatmapHitsounds.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. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
@ -16,7 +16,7 @@ namespace osu.Game.Skinning
Configuration = new DefaultSkinConfiguration(); Configuration = new DefaultSkinConfiguration();
} }
public override Drawable GetDrawableComponent(string componentName) => null; public override Drawable GetDrawableComponent(ISkinComponent component) => null;
public override Texture GetTexture(string componentName) => 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> /// </summary>
public interface ISkin public interface ISkin
{ {
Drawable GetDrawableComponent(string componentName); Drawable GetDrawableComponent(ISkinComponent component);
Texture GetTexture(string componentName); 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.Graphics.Textures;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Rulesets.Scoring;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Skinning namespace osu.Game.Skinning
@ -47,39 +48,30 @@ namespace osu.Game.Skinning
Samples?.Dispose(); Samples?.Dispose();
} }
public override Drawable GetDrawableComponent(string componentName) public override Drawable GetDrawableComponent(ISkinComponent component)
{ {
bool animatable = false; switch (component)
bool looping = true;
switch (componentName)
{ {
case "Play/Miss": case GameplaySkinComponent<HitResult> resultComponent:
componentName = "hit0"; switch (resultComponent.Component)
animatable = true; {
looping = false; case HitResult.Miss:
break; return this.GetAnimation("hit0", true, false);
case "Play/Meh": case HitResult.Meh:
componentName = "hit50"; return this.GetAnimation("hit50", true, false);
animatable = true;
looping = false;
break;
case "Play/Good": case HitResult.Good:
componentName = "hit100"; return this.GetAnimation("hit100", true, false);
animatable = true;
looping = false; case HitResult.Great:
break; return this.GetAnimation("hit300", true, false);
}
case "Play/Great":
componentName = "hit300";
animatable = true;
looping = false;
break; break;
} }
return this.GetAnimation(componentName, animatable, looping); return this.GetAnimation(component.LookupName, false, false);
} }
public override Texture GetTexture(string componentName) 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. // See the LICENCE file in the repository root for full licence text.
using System; using System;
@ -15,7 +15,7 @@ namespace osu.Game.Skinning
public virtual SkinConfiguration Configuration { get; protected set; } 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); 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. // See the LICENCE file in the repository root for full licence text.
using System; using System;
@ -125,7 +125,7 @@ namespace osu.Game.Skinning
public event Action SourceChanged; 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); public Texture GetTexture(string componentName) => CurrentSkin.Value.GetTexture(componentName);

View File

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

View File

@ -18,39 +18,39 @@ namespace osu.Game.Skinning
/// </summary> /// </summary>
public Drawable Drawable { get; private set; } public Drawable Drawable { get; private set; }
private readonly string componentName; private readonly ISkinComponent component;
private readonly ConfineMode confineMode; private readonly ConfineMode confineMode;
/// <summary> /// <summary>
/// Create a new skinnable drawable. /// Create a new skinnable drawable.
/// </summary> /// </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="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="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> /// <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) public SkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: this(name, allowFallback, confineMode) : this(component, allowFallback, confineMode)
{ {
createDefault = defaultImplementation; 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) : base(allowFallback)
{ {
componentName = name; this.component = component;
this.confineMode = confineMode; this.confineMode = confineMode;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
} }
private readonly Func<string, Drawable> createDefault; private readonly Func<ISkinComponent, Drawable> createDefault;
private readonly Cached scaling = new Cached(); private readonly Cached scaling = new Cached();
private bool isDefault; private bool isDefault;
protected virtual Drawable CreateDefault(string name) => createDefault(name); protected virtual Drawable CreateDefault(ISkinComponent component) => createDefault(component);
/// <summary> /// <summary>
/// Whether to apply size restrictions (specified via <see cref="confineMode"/>) to the default implementation. /// 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) protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{ {
Drawable = skin.GetDrawableComponent(componentName); Drawable = skin.GetDrawableComponent(component);
isDefault = false; isDefault = false;
if (Drawable == null && allowFallback) if (Drawable == null && allowFallback)
{ {
Drawable = CreateDefault(componentName); Drawable = CreateDefault(component);
isDefault = true; isDefault = true;
} }

View File

@ -19,11 +19,23 @@ namespace osu.Game.Skinning
[Resolved] [Resolved]
private TextureStore textures { get; set; } private TextureStore textures { get; set; }
public SkinnableSprite(string name, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) public SkinnableSprite(string textureName, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(name, allowFallback, confineMode) : base(new SpriteComponent(textureName), 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) };
private class SpriteComponent : ISkinComponent
{
private readonly string textureName;
public SpriteComponent(string textureName)
{
this.textureName = textureName;
}
public string LookupName => textureName;
}
} }
} }

View File

@ -8,8 +8,8 @@ namespace osu.Game.Skinning
{ {
public class SkinnableSpriteText : SkinnableDrawable, IHasText public class SkinnableSpriteText : SkinnableDrawable, IHasText
{ {
public SkinnableSpriteText(string name, Func<string, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) public SkinnableSpriteText(ISkinComponent component, Func<ISkinComponent, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(name, defaultImplementation, allowFallback, confineMode) : 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" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> <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.1" />
<PackageReference Include="ppy.osu.Framework" Version="2019.830.1" /> <PackageReference Include="ppy.osu.Framework" Version="2019.830.1" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.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" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.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.1" />
<PackageReference Include="ppy.osu.Framework" Version="2019.830.1" /> <PackageReference Include="ppy.osu.Framework" Version="2019.830.1" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.830.1" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2019.830.1" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.24.0" />