1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-16 06:52:55 +08:00

Merge branch 'keyboard_shortcuts' into jcramos/ctrl_shift_t

This commit is contained in:
Angela Zhang 2020-12-17 16:56:44 -06:00
commit d8864473b2
188 changed files with 1913 additions and 1380 deletions

View File

@ -52,6 +52,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.1202.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.1203.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2020.1212.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" /> <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="DiscordRichPresence" Version="1.0.166" /> <PackageReference Include="DiscordRichPresence" Version="1.0.169" />
<!-- .NET 3.1 SDK seems to cause issues with a runtime specification. This will likely be resolved in .NET 5. --> <!-- .NET 3.1 SDK seems to cause issues with a runtime specification. This will likely be resolved in .NET 5. -->
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" /> <PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="System.Runtime.Handles" Version="4.3.0" /> <PackageReference Include="System.Runtime.Handles" Version="4.3.0" />

View File

@ -4,6 +4,7 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.IO.Stores; using osu.Framework.IO.Stores;
using osu.Game.Rulesets.Catch.Skinning; using osu.Game.Rulesets.Catch.Skinning;
using osu.Game.Rulesets.Catch.Skinning.Legacy;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK.Graphics; using osuTK.Graphics;

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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
@ -12,8 +13,12 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
@ -24,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests
[Resolved] [Resolved]
private OsuConfigManager config { get; set; } private OsuConfigManager config { get; set; }
private Container droppedObjectContainer; private Container<CaughtObject> droppedObjectContainer;
private TestCatcher catcher; private TestCatcher catcher;
@ -37,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Tests
}; };
var trailContainer = new Container(); var trailContainer = new Container();
droppedObjectContainer = new Container(); droppedObjectContainer = new Container<CaughtObject>();
catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty); catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty);
Child = new Container Child = new Container
@ -52,6 +57,50 @@ namespace osu.Game.Rulesets.Catch.Tests
}; };
}); });
[Test]
public void TestCatcherHyperStateReverted()
{
DrawableCatchHitObject drawableObject1 = null;
DrawableCatchHitObject drawableObject2 = null;
JudgementResult result1 = null;
JudgementResult result2 = null;
AddStep("catch hyper fruit", () =>
{
attemptCatch(new Fruit { HyperDashTarget = new Fruit { X = 100 } }, out drawableObject1, out result1);
});
AddStep("catch normal fruit", () =>
{
attemptCatch(new Fruit(), out drawableObject2, out result2);
});
AddStep("revert second result", () =>
{
catcher.OnRevertResult(drawableObject2, result2);
});
checkHyperDash(true);
AddStep("revert first result", () =>
{
catcher.OnRevertResult(drawableObject1, result1);
});
checkHyperDash(false);
}
[Test]
public void TestCatcherAnimationStateReverted()
{
DrawableCatchHitObject drawableObject = null;
JudgementResult result = null;
AddStep("catch kiai fruit", () =>
{
attemptCatch(new TestKiaiFruit(), out drawableObject, out result);
});
checkState(CatcherAnimationState.Kiai);
AddStep("revert result", () =>
{
catcher.OnRevertResult(drawableObject, result);
});
checkState(CatcherAnimationState.Idle);
}
[Test] [Test]
public void TestCatcherCatchWidth() public void TestCatcherCatchWidth()
{ {
@ -149,13 +198,22 @@ namespace osu.Game.Rulesets.Catch.Tests
AddAssert("fruits are dropped", () => !catcher.CaughtObjects.Any() && droppedObjectContainer.Count == 10); AddAssert("fruits are dropped", () => !catcher.CaughtObjects.Any() && droppedObjectContainer.Count == 10);
} }
[TestCase(true)] [Test]
[TestCase(false)] public void TestHitLightingColour()
public void TestHitLighting(bool enabled)
{ {
AddStep($"{(enabled ? "enable" : "disable")} hit lighting", () => config.Set(OsuSetting.HitLighting, enabled)); var fruitColour = SkinConfiguration.DefaultComboColours[1];
AddStep("enable hit lighting", () => config.Set(OsuSetting.HitLighting, true));
AddStep("catch fruit", () => attemptCatch(new Fruit())); AddStep("catch fruit", () => attemptCatch(new Fruit()));
AddAssert("check hit lighting", () => catcher.ChildrenOfType<HitExplosion>().Any() == enabled); AddAssert("correct hit lighting colour", () =>
catcher.ChildrenOfType<HitExplosion>().First()?.ObjectColour == fruitColour);
}
[Test]
public void TestHitLightingDisabled()
{
AddStep("disable hit lighting", () => config.Set(OsuSetting.HitLighting, false));
AddStep("catch fruit", () => attemptCatch(new Fruit()));
AddAssert("no hit lighting", () => !catcher.ChildrenOfType<HitExplosion>().Any());
} }
private void checkPlate(int count) => AddAssert($"{count} objects on the plate", () => catcher.CaughtObjects.Count() == count); private void checkPlate(int count) => AddAssert($"{count} objects on the plate", () => catcher.CaughtObjects.Count() == count);
@ -166,17 +224,60 @@ namespace osu.Game.Rulesets.Catch.Tests
private void attemptCatch(CatchHitObject hitObject, int count = 1) private void attemptCatch(CatchHitObject hitObject, int count = 1)
{ {
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
catcher.AttemptCatch(hitObject); attemptCatch(hitObject, out _, out _);
}
private void attemptCatch(CatchHitObject hitObject, out DrawableCatchHitObject drawableObject, out JudgementResult result)
{
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
drawableObject = createDrawableObject(hitObject);
result = createResult(hitObject);
applyResult(drawableObject, result);
}
private void applyResult(DrawableCatchHitObject drawableObject, JudgementResult result)
{
// Load DHO to set colour of hit explosion correctly
Add(drawableObject);
drawableObject.OnLoadComplete += _ =>
{
catcher.OnNewResult(drawableObject, result);
drawableObject.Expire();
};
}
private JudgementResult createResult(CatchHitObject hitObject)
{
return new CatchJudgementResult(hitObject, hitObject.CreateJudgement())
{
Type = catcher.CanCatch(hitObject) ? HitResult.Great : HitResult.Miss
};
}
private DrawableCatchHitObject createDrawableObject(CatchHitObject hitObject)
{
switch (hitObject)
{
case Banana banana:
return new DrawableBanana(banana);
case Droplet droplet:
return new DrawableDroplet(droplet);
case Fruit fruit:
return new DrawableFruit(fruit);
default:
throw new ArgumentOutOfRangeException(nameof(hitObject));
}
} }
public class TestCatcher : Catcher public class TestCatcher : Catcher
{ {
public IEnumerable<DrawablePalpableCatchHitObject> CaughtObjects => this.ChildrenOfType<DrawablePalpableCatchHitObject>(); public IEnumerable<CaughtObject> CaughtObjects => this.ChildrenOfType<CaughtObject>();
public TestCatcher(Container trailsTarget, Container droppedObjectTarget, BeatmapDifficulty difficulty) public TestCatcher(Container trailsTarget, Container<CaughtObject> droppedObjectTarget, BeatmapDifficulty difficulty)
: base(trailsTarget, droppedObjectTarget, difficulty) : base(trailsTarget, droppedObjectTarget, difficulty)
{ {
} }

View File

@ -15,7 +15,6 @@ using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
@ -58,10 +57,9 @@ namespace osu.Game.Rulesets.Catch.Tests
Schedule(() => Schedule(() =>
{ {
bool caught = area.AttemptCatch(fruit); area.OnNewResult(drawable, new CatchJudgementResult(fruit, new CatchJudgement())
area.OnNewResult(drawable, new JudgementResult(fruit, new CatchJudgement())
{ {
Type = caught ? HitResult.Great : HitResult.Miss Type = area.MovableCatcher.CanCatch(fruit) ? HitResult.Great : HitResult.Miss
}); });
drawable.Expire(); drawable.Expire();
@ -75,7 +73,10 @@ namespace osu.Game.Rulesets.Catch.Tests
SetContents(() => SetContents(() =>
{ {
var droppedObjectContainer = new Container(); var droppedObjectContainer = new Container<CaughtObject>
{
RelativeSizeAxes = Axes.Both
};
return new CatchInputManager(catchRuleset) return new CatchInputManager(catchRuleset)
{ {
@ -101,7 +102,7 @@ namespace osu.Game.Rulesets.Catch.Tests
private class TestCatcherArea : CatcherArea private class TestCatcherArea : CatcherArea
{ {
public TestCatcherArea(Container droppedObjectContainer, BeatmapDifficulty beatmapDifficulty) public TestCatcherArea(Container<CaughtObject> droppedObjectContainer, BeatmapDifficulty beatmapDifficulty)
: base(droppedObjectContainer, beatmapDifficulty) : base(droppedObjectContainer, beatmapDifficulty)
{ {
} }

View File

@ -5,19 +5,20 @@ using NUnit.Framework;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
{ {
public class TestSceneFruitRandomness : OsuTestScene public class TestSceneFruitRandomness : OsuTestScene
{ {
private readonly TestDrawableFruit drawableFruit; private readonly DrawableFruit drawableFruit;
private readonly TestDrawableBanana drawableBanana; private readonly DrawableBanana drawableBanana;
public TestSceneFruitRandomness() public TestSceneFruitRandomness()
{ {
drawableFruit = new TestDrawableFruit(new Fruit()); drawableFruit = new DrawableFruit(new Fruit());
drawableBanana = new TestDrawableBanana(new Banana()); drawableBanana = new DrawableBanana(new Banana());
Add(new TestDrawableCatchHitObjectSpecimen(drawableFruit) { X = -200 }); Add(new TestDrawableCatchHitObjectSpecimen(drawableFruit) { X = -200 });
Add(new TestDrawableCatchHitObjectSpecimen(drawableBanana)); Add(new TestDrawableCatchHitObjectSpecimen(drawableBanana));
@ -37,16 +38,16 @@ namespace osu.Game.Rulesets.Catch.Tests
float fruitRotation = 0; float fruitRotation = 0;
float bananaRotation = 0; float bananaRotation = 0;
float bananaScale = 0; Vector2 bananaSize = new Vector2();
Color4 bananaColour = new Color4(); Color4 bananaColour = new Color4();
AddStep("Initialize start time", () => AddStep("Initialize start time", () =>
{ {
drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time; drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time;
fruitRotation = drawableFruit.InnerRotation; fruitRotation = drawableFruit.DisplayRotation;
bananaRotation = drawableBanana.InnerRotation; bananaRotation = drawableBanana.DisplayRotation;
bananaScale = drawableBanana.InnerScale; bananaSize = drawableBanana.DisplaySize;
bananaColour = drawableBanana.AccentColour.Value; bananaColour = drawableBanana.AccentColour.Value;
}); });
@ -55,9 +56,9 @@ namespace osu.Game.Rulesets.Catch.Tests
drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = another_start_time; drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = another_start_time;
}); });
AddAssert("fruit rotation is changed", () => drawableFruit.InnerRotation != fruitRotation); AddAssert("fruit rotation is changed", () => drawableFruit.DisplayRotation != fruitRotation);
AddAssert("banana rotation is changed", () => drawableBanana.InnerRotation != bananaRotation); AddAssert("banana rotation is changed", () => drawableBanana.DisplayRotation != bananaRotation);
AddAssert("banana scale is changed", () => drawableBanana.InnerScale != bananaScale); AddAssert("banana size is changed", () => drawableBanana.DisplaySize != bananaSize);
AddAssert("banana colour is changed", () => drawableBanana.AccentColour.Value != bananaColour); AddAssert("banana colour is changed", () => drawableBanana.AccentColour.Value != bananaColour);
AddStep("reset start time", () => AddStep("reset start time", () =>
@ -65,32 +66,11 @@ namespace osu.Game.Rulesets.Catch.Tests
drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time; drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time;
}); });
AddAssert("rotation and scale restored", () => AddAssert("rotation and size restored", () =>
drawableFruit.InnerRotation == fruitRotation && drawableFruit.DisplayRotation == fruitRotation &&
drawableBanana.InnerRotation == bananaRotation && drawableBanana.DisplayRotation == bananaRotation &&
drawableBanana.InnerScale == bananaScale && drawableBanana.DisplaySize == bananaSize &&
drawableBanana.AccentColour.Value == bananaColour); drawableBanana.AccentColour.Value == bananaColour);
} }
private class TestDrawableFruit : DrawableFruit
{
public float InnerRotation => ScaleContainer.Rotation;
public TestDrawableFruit(Fruit h)
: base(h)
{
}
}
private class TestDrawableBanana : DrawableBanana
{
public float InnerRotation => ScaleContainer.Rotation;
public float InnerScale => ScaleContainer.Scale.X;
public TestDrawableBanana(Banana h)
: base(h)
{
}
}
} }
} }

View File

@ -13,6 +13,7 @@ using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Skinning; using osu.Game.Rulesets.Catch.Skinning;
using osu.Game.Rulesets.Catch.Skinning.Legacy;
using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Catch.UI;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
@ -117,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.Tests
AddStep("create hyper-dashing catcher", () => AddStep("create hyper-dashing catcher", () =>
{ {
Child = setupSkinHierarchy(catcherArea = new CatcherArea(new Container()) Child = setupSkinHierarchy(catcherArea = new CatcherArea(new Container<CaughtObject>())
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

View File

@ -21,7 +21,7 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using System; using System;
using osu.Game.Rulesets.Catch.Skinning; using osu.Game.Rulesets.Catch.Skinning.Legacy;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch namespace osu.Game.Rulesets.Catch

View File

@ -5,11 +5,8 @@ namespace osu.Game.Rulesets.Catch
{ {
public enum CatchSkinComponents public enum CatchSkinComponents
{ {
FruitBananas, Fruit,
FruitApple, Banana,
FruitGrapes,
FruitOrange,
FruitPear,
Droplet, Droplet,
CatcherIdle, CatcherIdle,
CatcherFail, CatcherFail,

View File

@ -0,0 +1,28 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using JetBrains.Annotations;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Catch.Judgements
{
public class CatchJudgementResult : JudgementResult
{
/// <summary>
/// The catcher animation state prior to this judgement.
/// </summary>
public CatcherAnimationState CatcherAnimationState;
/// <summary>
/// Whether the catcher was hyper dashing prior to this judgement.
/// </summary>
public bool CatcherHyperDash;
public CatchJudgementResult([NotNull] HitObject hitObject, [NotNull] Judgement judgement)
: base(hitObject, judgement)
{
}
}
}

View File

@ -14,7 +14,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects namespace osu.Game.Rulesets.Catch.Objects
{ {
public class Banana : Fruit, IHasComboInformation public class Banana : PalpableCatchHitObject, IHasComboInformation
{ {
/// <summary> /// <summary>
/// Index of banana in current shower. /// Index of banana in current shower.

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.
using osu.Game.Rulesets.Catch.Skinning.Default;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
/// <summary>
/// Represents a <see cref="Banana"/> caught by the catcher.
/// </summary>
public class CaughtBanana : CaughtObject
{
public CaughtBanana()
: base(CatchSkinComponents.Banana, _ => new BananaPiece())
{
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Catch.Skinning.Default;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
/// <summary>
/// Represents a <see cref="Droplet"/> caught by the catcher.
/// </summary>
public class CaughtDroplet : CaughtObject
{
public override bool StaysOnPlate => false;
public CaughtDroplet()
: base(CatchSkinComponents.Droplet, _ => new DropletPiece())
{
}
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Game.Rulesets.Catch.Skinning.Default;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
/// <summary>
/// Represents a <see cref="Fruit"/> caught by the catcher.
/// </summary>
public class CaughtFruit : CaughtObject, IHasFruitState
{
public Bindable<FruitVisualRepresentation> VisualRepresentation { get; } = new Bindable<FruitVisualRepresentation>();
public CaughtFruit()
: base(CatchSkinComponents.Fruit, _ => new FruitPiece())
{
}
public override void CopyStateFrom(IHasCatchObjectState objectState)
{
base.CopyStateFrom(objectState);
var fruitState = (IHasFruitState)objectState;
VisualRepresentation.Value = fruitState.VisualRepresentation.Value;
}
}
}

View File

@ -0,0 +1,64 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
/// <summary>
/// Represents a <see cref="PalpableCatchHitObject"/> caught by the catcher.
/// </summary>
[Cached(typeof(IHasCatchObjectState))]
public abstract class CaughtObject : SkinnableDrawable, IHasCatchObjectState
{
public PalpableCatchHitObject HitObject { get; private set; }
public Bindable<Color4> AccentColour { get; } = new Bindable<Color4>();
public Bindable<bool> HyperDash { get; } = new Bindable<bool>();
public Vector2 DisplaySize => Size * Scale;
public float DisplayRotation => Rotation;
/// <summary>
/// Whether this hit object should stay on the catcher plate when the object is caught by the catcher.
/// </summary>
public virtual bool StaysOnPlate => true;
public override bool RemoveWhenNotAlive => true;
protected CaughtObject(CatchSkinComponents skinComponent, Func<ISkinComponent, Drawable> defaultImplementation)
: base(new CatchSkinComponent(skinComponent), defaultImplementation)
{
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.None;
Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
}
/// <summary>
/// Copies the hit object visual state from another <see cref="IHasCatchObjectState"/> object.
/// </summary>
public virtual void CopyStateFrom(IHasCatchObjectState objectState)
{
HitObject = objectState.HitObject;
Scale = Vector2.Divide(objectState.DisplaySize, Size);
Rotation = objectState.DisplayRotation;
AccentColour.Value = objectState.AccentColour.Value;
HyperDash.Value = objectState.HyperDash.Value;
}
protected override void FreeAfterUse()
{
ClearTransforms();
Alpha = 1;
base.FreeAfterUse();
}
}
}

View File

@ -2,14 +2,15 @@
// 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 JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Skinning.Default;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch.Objects.Drawables namespace osu.Game.Rulesets.Catch.Objects.Drawables
{ {
public class DrawableBanana : DrawableFruit public class DrawableBanana : DrawablePalpableCatchHitObject
{ {
protected override FruitVisualRepresentation GetVisualRepresentation(int indexInBeatmap) => FruitVisualRepresentation.Banana;
public DrawableBanana() public DrawableBanana()
: this(null) : this(null)
{ {
@ -20,6 +21,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
{ {
} }
[BackgroundDependencyLoader]
private void load()
{
ScalingContainer.Child = new SkinnableDrawable(
new CatchSkinComponent(CatchSkinComponents.Banana),
_ => new BananaPiece());
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
@ -35,10 +44,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
const float end_scale = 0.6f; const float end_scale = 0.6f;
const float random_scale_range = 1.6f; const float random_scale_range = 1.6f;
ScaleContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3))) ScalingContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3)))
.Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt); .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt);
ScaleContainer.RotateTo(getRandomAngle(1)) ScalingContainer.RotateTo(getRandomAngle(1))
.Then() .Then()
.RotateTo(getRandomAngle(2), HitObject.TimePreempt); .RotateTo(getRandomAngle(2), HitObject.TimePreempt);

View File

@ -5,7 +5,9 @@ using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Utils; using osu.Game.Utils;
@ -48,9 +50,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
public Func<CatchHitObject, bool> CheckPosition; public Func<CatchHitObject, bool> CheckPosition;
public bool IsOnPlate; protected override JudgementResult CreateResult(Judgement judgement) => new CatchJudgementResult(HitObject, judgement);
public override bool RemoveWhenNotAlive => IsOnPlate;
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {

View File

@ -4,15 +4,13 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osu.Game.Rulesets.Catch.Skinning.Default;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch.Objects.Drawables namespace osu.Game.Rulesets.Catch.Objects.Drawables
{ {
public class DrawableDroplet : DrawablePalpableCatchHitObject public class DrawableDroplet : DrawablePalpableCatchHitObject
{ {
public override bool StaysOnPlate => false;
public DrawableDroplet() public DrawableDroplet()
: this(null) : this(null)
{ {
@ -26,17 +24,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
HyperDash.BindValueChanged(_ => updatePiece(), true); ScalingContainer.Child = new SkinnableDrawable(
}
private void updatePiece()
{
ScaleContainer.Child = new SkinnableDrawable(
new CatchSkinComponent(CatchSkinComponents.Droplet), new CatchSkinComponent(CatchSkinComponents.Droplet),
_ => new DropletPiece _ => new DropletPiece());
{
HyperDash = { BindTarget = HyperDash }
});
} }
protected override void UpdateInitialTransforms() protected override void UpdateInitialTransforms()
@ -47,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
float startRotation = RandomSingle(1) * 20; float startRotation = RandomSingle(1) * 20;
double duration = HitObject.TimePreempt + 2000; double duration = HitObject.TimePreempt + 2000;
ScaleContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration); ScalingContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration);
} }
} }
} }

View File

@ -1,21 +1,18 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osu.Game.Rulesets.Catch.Skinning.Default;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch.Objects.Drawables namespace osu.Game.Rulesets.Catch.Objects.Drawables
{ {
public class DrawableFruit : DrawablePalpableCatchHitObject public class DrawableFruit : DrawablePalpableCatchHitObject, IHasFruitState
{ {
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>(); public Bindable<FruitVisualRepresentation> VisualRepresentation { get; } = new Bindable<FruitVisualRepresentation>();
protected virtual FruitVisualRepresentation GetVisualRepresentation(int indexInBeatmap) => (FruitVisualRepresentation)(indexInBeatmap % 4);
public DrawableFruit() public DrawableFruit()
: this(null) : this(null)
@ -32,53 +29,19 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
{ {
IndexInBeatmap.BindValueChanged(change => IndexInBeatmap.BindValueChanged(change =>
{ {
VisualRepresentation.Value = GetVisualRepresentation(change.NewValue); VisualRepresentation.Value = (FruitVisualRepresentation)(change.NewValue % 4);
}, true); }, true);
VisualRepresentation.BindValueChanged(_ => updatePiece()); ScalingContainer.Child = new SkinnableDrawable(
HyperDash.BindValueChanged(_ => updatePiece(), true); new CatchSkinComponent(CatchSkinComponents.Fruit),
_ => new FruitPiece());
} }
protected override void UpdateInitialTransforms() protected override void UpdateInitialTransforms()
{ {
base.UpdateInitialTransforms(); base.UpdateInitialTransforms();
ScaleContainer.RotateTo((RandomSingle(1) - 0.5f) * 40); ScalingContainer.RotateTo((RandomSingle(1) - 0.5f) * 40);
}
private void updatePiece()
{
ScaleContainer.Child = new SkinnableDrawable(
new CatchSkinComponent(getComponent(VisualRepresentation.Value)),
_ => new FruitPiece
{
VisualRepresentation = { BindTarget = VisualRepresentation },
HyperDash = { BindTarget = HyperDash },
});
}
private CatchSkinComponents getComponent(FruitVisualRepresentation hitObjectVisualRepresentation)
{
switch (hitObjectVisualRepresentation)
{
case FruitVisualRepresentation.Pear:
return CatchSkinComponents.FruitPear;
case FruitVisualRepresentation.Grape:
return CatchSkinComponents.FruitGrapes;
case FruitVisualRepresentation.Pineapple:
return CatchSkinComponents.FruitApple;
case FruitVisualRepresentation.Raspberry:
return CatchSkinComponents.FruitOrange;
case FruitVisualRepresentation.Banana:
return CatchSkinComponents.FruitBananas;
default:
throw new ArgumentOutOfRangeException(nameof(hitObjectVisualRepresentation), hitObjectVisualRepresentation, null);
}
} }
} }
@ -88,6 +51,5 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
Grape, Grape,
Pineapple, Pineapple,
Raspberry, Raspberry,
Banana // banananananannaanana
} }
} }

View File

@ -7,32 +7,36 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osuTK; using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawables namespace osu.Game.Rulesets.Catch.Objects.Drawables
{ {
public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject [Cached(typeof(IHasCatchObjectState))]
public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject, IHasCatchObjectState
{ {
public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject; public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject;
public readonly Bindable<bool> HyperDash = new Bindable<bool>(); Bindable<Color4> IHasCatchObjectState.AccentColour => AccentColour;
public readonly Bindable<float> ScaleBindable = new Bindable<float>(1); public Bindable<bool> HyperDash { get; } = new Bindable<bool>();
public readonly Bindable<int> IndexInBeatmap = new Bindable<int>(); public Bindable<float> ScaleBindable { get; } = new Bindable<float>(1);
public Bindable<int> IndexInBeatmap { get; } = new Bindable<int>();
/// <summary> /// <summary>
/// The multiplicative factor applied to <see cref="ScaleContainer"/> scale relative to <see cref="HitObject"/> scale. /// The multiplicative factor applied to <see cref="Drawable.Scale"/> relative to <see cref="HitObject"/> scale.
/// </summary> /// </summary>
protected virtual float ScaleFactor => 1; protected virtual float ScaleFactor => 1;
/// <summary> /// <summary>
/// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. /// The container internal transforms (such as scaling based on the circle size) are applied to.
/// </summary> /// </summary>
public virtual bool StaysOnPlate => true; protected readonly Container ScalingContainer;
public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; public Vector2 DisplaySize => ScalingContainer.Size * ScalingContainer.Scale;
protected readonly Container ScaleContainer; public float DisplayRotation => ScalingContainer.Rotation;
protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h)
: base(h) : base(h)
@ -40,11 +44,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
Origin = Anchor.Centre; Origin = Anchor.Centre;
Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
AddInternal(ScaleContainer = new Container AddInternal(ScalingContainer = new Container
{ {
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2)
}); });
} }
@ -53,12 +57,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
{ {
XBindable.BindValueChanged(x => XBindable.BindValueChanged(x =>
{ {
if (!IsOnPlate) X = x.NewValue; X = x.NewValue;
}, true); }, true);
ScaleBindable.BindValueChanged(scale => ScaleBindable.BindValueChanged(scale =>
{ {
ScaleContainer.Scale = new Vector2(scale.NewValue * ScaleFactor); ScalingContainer.Scale = new Vector2(scale.NewValue * ScaleFactor);
Size = DisplaySize;
}, true); }, true);
IndexInBeatmap.BindValueChanged(_ => UpdateComboColour()); IndexInBeatmap.BindValueChanged(_ => UpdateComboColour());

View File

@ -0,0 +1,25 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
/// <summary>
/// Provides a visual state of a <see cref="PalpableCatchHitObject"/>.
/// </summary>
public interface IHasCatchObjectState
{
PalpableCatchHitObject HitObject { get; }
Bindable<Color4> AccentColour { get; }
Bindable<bool> HyperDash { get; }
Vector2 DisplaySize { get; }
float DisplayRotation { get; }
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
/// <summary>
/// Provides a visual state of a <see cref="Fruit"/>.
/// </summary>
public interface IHasFruitState : IHasCatchObjectState
{
Bindable<FruitVisualRepresentation> VisualRepresentation { get; }
}
}

View File

@ -1,30 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osuTK;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
{
public class BananaPiece : PulpFormation
{
public BananaPiece()
{
InternalChildren = new Drawable[]
{
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(SMALL_PULP),
Y = -0.3f
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_4 * 0.8f, LARGE_PULP_4 * 2.5f),
Y = 0.05f,
},
};
}
}
}

View File

@ -1,37 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
using osuTK;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
{
public class DropletPiece : CompositeDrawable
{
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
public DropletPiece()
{
Size = new Vector2(CatchHitObject.OBJECT_RADIUS / 2);
}
[BackgroundDependencyLoader]
private void load(DrawableHitObject drawableObject)
{
InternalChild = new Pulp
{
RelativeSizeAxes = Axes.Both,
AccentColour = { BindTarget = drawableObject.AccentColour }
};
if (HyperDash.Value)
{
AddInternal(new HyperDropletBorderPiece());
}
}
}
}

View File

@ -1,79 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
{
internal class FruitPiece : CompositeDrawable
{
/// <summary>
/// Because we're adding a border around the fruit, we need to scale down some.
/// </summary>
public const float RADIUS_ADJUST = 1.1f;
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>();
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
[CanBeNull]
private DrawableCatchHitObject drawableHitObject;
[CanBeNull]
private BorderPiece borderPiece;
public FruitPiece()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load([CanBeNull] DrawableHitObject drawable)
{
drawableHitObject = (DrawableCatchHitObject)drawable;
AddInternal(getFruitFor(VisualRepresentation.Value));
// if it is not part of a DHO, the border is always invisible.
if (drawableHitObject != null)
AddInternal(borderPiece = new BorderPiece());
if (HyperDash.Value)
AddInternal(new HyperBorderPiece());
}
protected override void Update()
{
if (borderPiece != null && drawableHitObject?.HitObject != null)
borderPiece.Alpha = (float)Math.Clamp((drawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1);
}
private Drawable getFruitFor(FruitVisualRepresentation representation)
{
switch (representation)
{
case FruitVisualRepresentation.Pear:
return new PearPiece();
case FruitVisualRepresentation.Grape:
return new GrapePiece();
case FruitVisualRepresentation.Pineapple:
return new PineapplePiece();
case FruitVisualRepresentation.Banana:
return new BananaPiece();
case FruitVisualRepresentation.Raspberry:
return new RaspberryPiece();
}
return Empty();
}
}
}

View File

@ -1,42 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osuTK;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
{
public class GrapePiece : PulpFormation
{
public GrapePiece()
{
InternalChildren = new Drawable[]
{
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(SMALL_PULP),
Y = -0.25f,
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_3),
Position = PositionAt(0, DISTANCE_FROM_CENTRE_3),
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_3),
Position = PositionAt(120, DISTANCE_FROM_CENTRE_3),
},
new Pulp
{
Size = new Vector2(LARGE_PULP_3),
AccentColour = { BindTarget = AccentColour },
Position = PositionAt(240, DISTANCE_FROM_CENTRE_3),
},
};
}
}
}

View File

@ -1,42 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osuTK;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
{
public class PearPiece : PulpFormation
{
public PearPiece()
{
InternalChildren = new Drawable[]
{
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(SMALL_PULP),
Y = -0.33f,
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_3),
Position = PositionAt(60, DISTANCE_FROM_CENTRE_3),
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_3),
Position = PositionAt(180, DISTANCE_FROM_CENTRE_3),
},
new Pulp
{
Size = new Vector2(LARGE_PULP_3),
AccentColour = { BindTarget = AccentColour },
Position = PositionAt(300, DISTANCE_FROM_CENTRE_3),
},
};
}
}
}

View File

@ -1,48 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osuTK;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
{
public class PineapplePiece : PulpFormation
{
public PineapplePiece()
{
InternalChildren = new Drawable[]
{
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(SMALL_PULP),
Y = -0.3f,
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_4),
Position = PositionAt(45, DISTANCE_FROM_CENTRE_4),
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_4),
Position = PositionAt(135, DISTANCE_FROM_CENTRE_4),
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_4),
Position = PositionAt(225, DISTANCE_FROM_CENTRE_4),
},
new Pulp
{
Size = new Vector2(LARGE_PULP_4),
AccentColour = { BindTarget = AccentColour },
Position = PositionAt(315, DISTANCE_FROM_CENTRE_4),
},
};
}
}
}

View File

@ -1,48 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osuTK;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
{
public class RaspberryPiece : PulpFormation
{
public RaspberryPiece()
{
InternalChildren = new Drawable[]
{
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(SMALL_PULP),
Y = -0.34f,
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_4),
Position = PositionAt(0, DISTANCE_FROM_CENTRE_4),
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_4),
Position = PositionAt(90, DISTANCE_FROM_CENTRE_4),
},
new Pulp
{
AccentColour = { BindTarget = AccentColour },
Size = new Vector2(LARGE_PULP_4),
Position = PositionAt(180, DISTANCE_FROM_CENTRE_4),
},
new Pulp
{
Size = new Vector2(LARGE_PULP_4),
AccentColour = { BindTarget = AccentColour },
Position = PositionAt(270, DISTANCE_FROM_CENTRE_4),
},
};
}
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Catch.Skinning.Default
{
public class BananaPiece : CatchHitObjectPiece
{
protected override BorderPiece BorderPiece { get; }
public BananaPiece()
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new BananaPulpFormation
{
AccentColour = { BindTarget = AccentColour },
},
BorderPiece = new BorderPiece(),
};
}
}
}

View File

@ -0,0 +1,16 @@
// 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 osuTK;
namespace osu.Game.Rulesets.Catch.Skinning.Default
{
public class BananaPulpFormation : PulpFormation
{
public BananaPulpFormation()
{
AddPulp(new Vector2(0, -0.3f), new Vector2(SMALL_PULP));
AddPulp(new Vector2(0, 0.05f), new Vector2(LARGE_PULP_4 * 0.8f, LARGE_PULP_4 * 2.5f));
}
}
}

View File

@ -3,10 +3,11 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Catch.Objects;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Catch.Skinning.Default
{ {
public class BorderPiece : Circle public class BorderPiece : Circle
{ {

View File

@ -0,0 +1,54 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Skinning.Default
{
public abstract class CatchHitObjectPiece : CompositeDrawable
{
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
[Resolved]
protected IHasCatchObjectState ObjectState { get; private set; }
/// <summary>
/// A part of this piece that will be faded out while falling in the playfield.
/// </summary>
[CanBeNull]
protected virtual BorderPiece BorderPiece => null;
/// <summary>
/// A part of this piece that will be only visible when <see cref="HyperDash"/> is true.
/// </summary>
[CanBeNull]
protected virtual HyperBorderPiece HyperBorderPiece => null;
protected override void LoadComplete()
{
base.LoadComplete();
AccentColour.BindTo(ObjectState.AccentColour);
HyperDash.BindTo(ObjectState.HyperDash);
HyperDash.BindValueChanged(hyper =>
{
if (HyperBorderPiece != null)
HyperBorderPiece.Alpha = hyper.NewValue ? 1 : 0;
}, true);
}
protected override void Update()
{
if (BorderPiece != null)
BorderPiece.Alpha = (float)Math.Clamp((ObjectState.HitObject.StartTime - Time.Current) / 500, 0, 1);
}
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects;
using osuTK;
namespace osu.Game.Rulesets.Catch.Skinning.Default
{
public class DropletPiece : CatchHitObjectPiece
{
protected override HyperBorderPiece HyperBorderPiece { get; }
public DropletPiece()
{
Size = new Vector2(CatchHitObject.OBJECT_RADIUS / 2);
InternalChildren = new Drawable[]
{
new Pulp
{
RelativeSizeAxes = Axes.Both,
AccentColour = { BindTarget = AccentColour }
},
HyperBorderPiece = new HyperDropletBorderPiece()
};
}
}
}

View File

@ -0,0 +1,46 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects.Drawables;
namespace osu.Game.Rulesets.Catch.Skinning.Default
{
internal class FruitPiece : CatchHitObjectPiece
{
/// <summary>
/// Because we're adding a border around the fruit, we need to scale down some.
/// </summary>
public const float RADIUS_ADJUST = 1.1f;
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>();
protected override BorderPiece BorderPiece { get; }
protected override HyperBorderPiece HyperBorderPiece { get; }
public FruitPiece()
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
new FruitPulpFormation
{
AccentColour = { BindTarget = AccentColour },
VisualRepresentation = { BindTarget = VisualRepresentation }
},
BorderPiece = new BorderPiece(),
HyperBorderPiece = new HyperBorderPiece()
};
}
protected override void LoadComplete()
{
base.LoadComplete();
var fruitState = (IHasFruitState)ObjectState;
VisualRepresentation.BindTo(fruitState.VisualRepresentation);
}
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osuTK;
namespace osu.Game.Rulesets.Catch.Skinning.Default
{
public class FruitPulpFormation : PulpFormation
{
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>();
protected override void LoadComplete()
{
base.LoadComplete();
VisualRepresentation.BindValueChanged(setFormation, true);
}
private void setFormation(ValueChangedEvent<FruitVisualRepresentation> visualRepresentation)
{
Clear();
switch (visualRepresentation.NewValue)
{
case FruitVisualRepresentation.Pear:
AddPulp(new Vector2(0, -0.33f), new Vector2(SMALL_PULP));
AddPulp(PositionAt(60, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
AddPulp(PositionAt(180, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
AddPulp(PositionAt(300, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
break;
case FruitVisualRepresentation.Grape:
AddPulp(new Vector2(0, -0.25f), new Vector2(SMALL_PULP));
AddPulp(PositionAt(0, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
AddPulp(PositionAt(120, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
AddPulp(PositionAt(240, DISTANCE_FROM_CENTRE_3), new Vector2(LARGE_PULP_3));
break;
case FruitVisualRepresentation.Pineapple:
AddPulp(new Vector2(0, -0.3f), new Vector2(SMALL_PULP));
AddPulp(PositionAt(45, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
AddPulp(PositionAt(135, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
AddPulp(PositionAt(225, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
AddPulp(PositionAt(315, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
break;
case FruitVisualRepresentation.Raspberry:
AddPulp(new Vector2(0, -0.34f), new Vector2(SMALL_PULP));
AddPulp(PositionAt(0, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
AddPulp(PositionAt(90, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
AddPulp(PositionAt(180, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
AddPulp(PositionAt(270, DISTANCE_FROM_CENTRE_4), new Vector2(LARGE_PULP_4));
break;
}
}
}
}

View File

@ -4,7 +4,7 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Catch.UI;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Catch.Skinning.Default
{ {
public class HyperBorderPiece : BorderPiece public class HyperBorderPiece : BorderPiece
{ {

View File

@ -1,7 +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.
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Catch.Skinning.Default
{ {
public class HyperDropletBorderPiece : HyperBorderPiece public class HyperDropletBorderPiece : HyperBorderPiece
{ {

View File

@ -8,10 +8,12 @@ using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Catch.Skinning.Default
{ {
public class Pulp : Circle public class Pulp : Circle
{ {
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
public Pulp() public Pulp()
{ {
RelativePositionAxes = Axes.Both; RelativePositionAxes = Axes.Both;
@ -22,8 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
Colour = Color4.White.Opacity(0.9f); Colour = Color4.White.Opacity(0.9f);
} }
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();

View File

@ -2,19 +2,17 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Catch.Skinning.Default
{ {
public abstract class PulpFormation : CompositeDrawable public abstract class PulpFormation : CompositeDrawable
{ {
protected readonly IBindable<Color4> AccentColour = new Bindable<Color4>(); public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
protected const float LARGE_PULP_3 = 16f * FruitPiece.RADIUS_ADJUST; protected const float LARGE_PULP_3 = 16f * FruitPiece.RADIUS_ADJUST;
protected const float DISTANCE_FROM_CENTRE_3 = 0.15f; protected const float DISTANCE_FROM_CENTRE_3 = 0.15f;
@ -24,6 +22,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
protected const float SMALL_PULP = LARGE_PULP_3 / 2; protected const float SMALL_PULP = LARGE_PULP_3 / 2;
private int pulpsInUse;
protected PulpFormation() protected PulpFormation()
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
@ -33,11 +33,24 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces
distance * MathF.Sin(angle * MathF.PI / 180), distance * MathF.Sin(angle * MathF.PI / 180),
distance * MathF.Cos(angle * MathF.PI / 180)); distance * MathF.Cos(angle * MathF.PI / 180));
[BackgroundDependencyLoader] protected void Clear()
private void load(DrawableHitObject drawableObject)
{ {
DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject; for (int i = 0; i < pulpsInUse; i++)
AccentColour.BindTo(drawableCatchObject.AccentColour); InternalChildren[i].Alpha = 0;
pulpsInUse = 0;
}
protected void AddPulp(Vector2 position, Vector2 size)
{
if (pulpsInUse == InternalChildren.Count)
AddInternal(new Pulp { AccentColour = { BindTarget = AccentColour } });
var pulp = InternalChildren[pulpsInUse];
pulp.Position = position;
pulp.Size = size;
pulp.Alpha = 1;
pulpsInUse++;
} }
} }
} }

View File

@ -1,15 +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 Humanizer;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using static osu.Game.Skinning.LegacySkinConfiguration; using static osu.Game.Skinning.LegacySkinConfiguration;
namespace osu.Game.Rulesets.Catch.Skinning namespace osu.Game.Rulesets.Catch.Skinning.Legacy
{ {
public class CatchLegacySkinTransformer : LegacySkinTransformer public class CatchLegacySkinTransformer : LegacySkinTransformer
{ {
@ -40,20 +38,21 @@ namespace osu.Game.Rulesets.Catch.Skinning
switch (catchSkinComponent.Component) switch (catchSkinComponent.Component)
{ {
case CatchSkinComponents.FruitApple: case CatchSkinComponents.Fruit:
case CatchSkinComponents.FruitBananas: if (GetTexture("fruit-pear") != null)
case CatchSkinComponents.FruitOrange: return new LegacyFruitPiece();
case CatchSkinComponents.FruitGrapes:
case CatchSkinComponents.FruitPear: break;
var lookupName = catchSkinComponent.Component.ToString().Kebaberize();
if (GetTexture(lookupName) != null) case CatchSkinComponents.Banana:
return new LegacyFruitPiece(lookupName); if (GetTexture("fruit-bananas") != null)
return new LegacyBananaPiece();
break; break;
case CatchSkinComponents.Droplet: case CatchSkinComponents.Droplet:
if (GetTexture("fruit-drop") != null) if (GetTexture("fruit-drop") != null)
return new LegacyFruitPiece("fruit-drop") { Scale = new Vector2(0.8f) }; return new LegacyDropletPiece();
break; break;

View File

@ -9,7 +9,7 @@ using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using static osu.Game.Skinning.LegacySkinConfiguration; using static osu.Game.Skinning.LegacySkinConfiguration;
namespace osu.Game.Rulesets.Catch.Skinning namespace osu.Game.Rulesets.Catch.Skinning.Legacy
{ {
/// <summary> /// <summary>
/// A combo counter implementation that visually behaves almost similar to stable's osu!catch combo counter. /// A combo counter implementation that visually behaves almost similar to stable's osu!catch combo counter.

View File

@ -0,0 +1,45 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Game.Rulesets.Catch.Objects.Drawables;
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
{
internal class LegacyFruitPiece : LegacyCatchHitObjectPiece
{
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>();
protected override void LoadComplete()
{
base.LoadComplete();
var fruitState = (IHasFruitState)ObjectState;
VisualRepresentation.BindTo(fruitState.VisualRepresentation);
VisualRepresentation.BindValueChanged(visual => setTexture(visual.NewValue), true);
}
private void setTexture(FruitVisualRepresentation visualRepresentation)
{
switch (visualRepresentation)
{
case FruitVisualRepresentation.Pear:
SetTexture(Skin.GetTexture("fruit-pear"), Skin.GetTexture("fruit-pear-overlay"));
break;
case FruitVisualRepresentation.Grape:
SetTexture(Skin.GetTexture("fruit-grapes"), Skin.GetTexture("fruit-grapes-overlay"));
break;
case FruitVisualRepresentation.Pineapple:
SetTexture(Skin.GetTexture("fruit-apple"), Skin.GetTexture("fruit-apple-overlay"));
break;
case FruitVisualRepresentation.Raspberry:
SetTexture(Skin.GetTexture("fruit-orange"), Skin.GetTexture("fruit-orange-overlay"));
break;
}
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Textures;
namespace osu.Game.Rulesets.Catch.Skinning
{
public class LegacyBananaPiece : LegacyCatchHitObjectPiece
{
protected override void LoadComplete()
{
base.LoadComplete();
Texture texture = Skin.GetTexture("fruit-bananas");
Texture overlayTexture = Skin.GetTexture("fruit-bananas-overlay");
SetTexture(texture, overlayTexture);
}
}
}

View File

@ -0,0 +1,90 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Pooling;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Skinning
{
public abstract class LegacyCatchHitObjectPiece : PoolableDrawable
{
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
private readonly Sprite colouredSprite;
private readonly Sprite overlaySprite;
private readonly Sprite hyperSprite;
[Resolved]
protected ISkinSource Skin { get; private set; }
[Resolved]
protected IHasCatchObjectState ObjectState { get; private set; }
protected LegacyCatchHitObjectPiece()
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
colouredSprite = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
overlaySprite = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
hyperSprite = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Blending = BlendingParameters.Additive,
Depth = 1,
Alpha = 0,
Scale = new Vector2(1.2f),
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
AccentColour.BindTo(ObjectState.AccentColour);
HyperDash.BindTo(ObjectState.HyperDash);
hyperSprite.Colour = Skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashFruit)?.Value ??
Skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
Catcher.DEFAULT_HYPER_DASH_COLOUR;
AccentColour.BindValueChanged(colour =>
{
colouredSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue);
}, true);
HyperDash.BindValueChanged(hyper =>
{
hyperSprite.Alpha = hyper.NewValue ? 0.7f : 0;
}, true);
}
protected void SetTexture(Texture texture, Texture overlayTexture)
{
colouredSprite.Texture = texture;
overlaySprite.Texture = overlayTexture;
hyperSprite.Texture = texture;
}
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Textures;
using osuTK;
namespace osu.Game.Rulesets.Catch.Skinning
{
public class LegacyDropletPiece : LegacyCatchHitObjectPiece
{
public LegacyDropletPiece()
{
Scale = new Vector2(0.8f);
}
protected override void LoadComplete()
{
base.LoadComplete();
Texture texture = Skin.GetTexture("fruit-drop");
Texture overlayTexture = Skin.GetTexture("fruit-drop-overlay");
SetTexture(texture, overlayTexture);
}
}
}

View File

@ -1,83 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Skinning
{
internal class LegacyFruitPiece : CompositeDrawable
{
private readonly string lookupName;
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
private readonly Bindable<bool> hyperDash = new Bindable<bool>();
private Sprite colouredSprite;
public LegacyFruitPiece(string lookupName)
{
this.lookupName = lookupName;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(DrawableHitObject drawableObject, ISkinSource skin)
{
var drawableCatchObject = (DrawablePalpableCatchHitObject)drawableObject;
accentColour.BindTo(drawableCatchObject.AccentColour);
hyperDash.BindTo(drawableCatchObject.HyperDash);
InternalChildren = new Drawable[]
{
colouredSprite = new Sprite
{
Texture = skin.GetTexture(lookupName),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new Sprite
{
Texture = skin.GetTexture($"{lookupName}-overlay"),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
};
if (hyperDash.Value)
{
var hyperDashOverlay = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Blending = BlendingParameters.Additive,
Depth = 1,
Alpha = 0.7f,
Scale = new Vector2(1.2f),
Texture = skin.GetTexture(lookupName),
Colour = skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashFruit)?.Value ??
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
Catcher.DEFAULT_HYPER_DASH_COLOUR,
};
AddInternal(hyperDashOverlay);
}
}
protected override void LoadComplete()
{
base.LoadComplete();
accentColour.BindValueChanged(colour => colouredSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
}
}
}

View File

@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.UI
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation) public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation)
{ {
var droppedObjectContainer = new Container var droppedObjectContainer = new Container<CaughtObject>
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}; };
@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Catch.UI
((DrawableCatchHitObject)d).CheckPosition = checkIfWeCanCatch; ((DrawableCatchHitObject)d).CheckPosition = checkIfWeCanCatch;
} }
private bool checkIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj); private bool checkIfWeCanCatch(CatchHitObject obj) => CatcherArea.MovableCatcher.CanCatch(obj);
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
=> CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result); => CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result);

View File

@ -14,9 +14,11 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Skinning; using osu.Game.Rulesets.Catch.Skinning;
using osu.Game.Rulesets.Judgements;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -51,9 +53,15 @@ namespace osu.Game.Rulesets.Catch.UI
private CatcherTrailDisplay trails; private CatcherTrailDisplay trails;
private readonly Container droppedObjectTarget; /// <summary>
/// Contains caught objects on the plate.
/// </summary>
private readonly Container<CaughtObject> caughtObjectContainer;
private readonly Container<DrawablePalpableCatchHitObject> caughtFruitContainer; /// <summary>
/// Contains objects dropped from the plate.
/// </summary>
private readonly Container<CaughtObject> droppedObjectTarget;
public CatcherAnimationState CurrentState { get; private set; } public CatcherAnimationState CurrentState { get; private set; }
@ -106,7 +114,11 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly DrawablePool<HitExplosion> hitExplosionPool; private readonly DrawablePool<HitExplosion> hitExplosionPool;
private readonly Container<HitExplosion> hitExplosionContainer; private readonly Container<HitExplosion> hitExplosionContainer;
public Catcher([NotNull] Container trailsTarget, [NotNull] Container droppedObjectTarget, BeatmapDifficulty difficulty = null) private readonly DrawablePool<CaughtFruit> caughtFruitPool;
private readonly DrawablePool<CaughtBanana> caughtBananaPool;
private readonly DrawablePool<CaughtDroplet> caughtDropletPool;
public Catcher([NotNull] Container trailsTarget, [NotNull] Container<CaughtObject> droppedObjectTarget, BeatmapDifficulty difficulty = null)
{ {
this.trailsTarget = trailsTarget; this.trailsTarget = trailsTarget;
this.droppedObjectTarget = droppedObjectTarget; this.droppedObjectTarget = droppedObjectTarget;
@ -122,7 +134,11 @@ namespace osu.Game.Rulesets.Catch.UI
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
hitExplosionPool = new DrawablePool<HitExplosion>(10), hitExplosionPool = new DrawablePool<HitExplosion>(10),
caughtFruitContainer = new Container<DrawablePalpableCatchHitObject> caughtFruitPool = new DrawablePool<CaughtFruit>(50),
caughtBananaPool = new DrawablePool<CaughtBanana>(100),
// less capacity is needed compared to fruit because droplet is not stacked
caughtDropletPool = new DrawablePool<CaughtDroplet>(25),
caughtObjectContainer = new Container<CaughtObject>
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre, Origin = Anchor.BottomCentre,
@ -170,7 +186,7 @@ namespace osu.Game.Rulesets.Catch.UI
/// <summary> /// <summary>
/// Creates proxied content to be displayed beneath hitobjects. /// Creates proxied content to be displayed beneath hitobjects.
/// </summary> /// </summary>
public Drawable CreateProxiedContent() => caughtFruitContainer.CreateProxy(); public Drawable CreateProxiedContent() => caughtObjectContainer.CreateProxy();
/// <summary> /// <summary>
/// Calculates the scale of the catcher based off the provided beatmap difficulty. /// Calculates the scale of the catcher based off the provided beatmap difficulty.
@ -190,11 +206,9 @@ namespace osu.Game.Rulesets.Catch.UI
internal static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(calculateScale(difficulty)); internal static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(calculateScale(difficulty));
/// <summary> /// <summary>
/// Let the catcher attempt to catch a fruit. /// Determine if this catcher can catch a <see cref="CatchHitObject"/> in the current position.
/// </summary> /// </summary>
/// <param name="hitObject">The fruit to catch.</param> public bool CanCatch(CatchHitObject hitObject)
/// <returns>Whether the catch is possible.</returns>
public bool AttemptCatch(CatchHitObject hitObject)
{ {
if (!(hitObject is PalpableCatchHitObject fruit)) if (!(hitObject is PalpableCatchHitObject fruit))
return false; return false;
@ -205,21 +219,38 @@ namespace osu.Game.Rulesets.Catch.UI
var catchObjectPosition = fruit.X; var catchObjectPosition = fruit.X;
var catcherPosition = Position.X; var catcherPosition = Position.X;
var validCatch = return catchObjectPosition >= catcherPosition - halfCatchWidth &&
catchObjectPosition >= catcherPosition - halfCatchWidth &&
catchObjectPosition <= catcherPosition + halfCatchWidth; catchObjectPosition <= catcherPosition + halfCatchWidth;
}
if (validCatch) public void OnNewResult(DrawableCatchHitObject drawableObject, JudgementResult result)
placeCaughtObject(fruit); {
var catchResult = (CatchJudgementResult)result;
catchResult.CatcherAnimationState = CurrentState;
catchResult.CatcherHyperDash = HyperDashing;
if (!(drawableObject is DrawablePalpableCatchHitObject palpableObject)) return;
var hitObject = palpableObject.HitObject;
if (result.IsHit)
{
var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplaySize.X / 2);
placeCaughtObject(palpableObject, positionInStack);
if (hitLighting.Value)
addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value);
}
// droplet doesn't affect the catcher state // droplet doesn't affect the catcher state
if (fruit is TinyDroplet) return validCatch; if (hitObject is TinyDroplet) return;
if (validCatch && fruit.HyperDash) if (result.IsHit && hitObject.HyperDash)
{ {
var target = fruit.HyperDashTarget; var target = hitObject.HyperDashTarget;
var timeDifference = target.StartTime - fruit.StartTime; var timeDifference = target.StartTime - hitObject.StartTime;
double positionDifference = target.X - catcherPosition; double positionDifference = target.X - X;
var velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0); var velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
SetHyperDashState(Math.Abs(velocity), target.X); SetHyperDashState(Math.Abs(velocity), target.X);
@ -227,12 +258,30 @@ namespace osu.Game.Rulesets.Catch.UI
else else
SetHyperDashState(); SetHyperDashState();
if (validCatch) if (result.IsHit)
updateState(fruit.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle); updateState(hitObject.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle);
else if (!(fruit is Banana)) else if (!(hitObject is Banana))
updateState(CatcherAnimationState.Fail); updateState(CatcherAnimationState.Fail);
}
return validCatch; public void OnRevertResult(DrawableCatchHitObject drawableObject, JudgementResult result)
{
var catchResult = (CatchJudgementResult)result;
if (CurrentState != catchResult.CatcherAnimationState)
updateState(catchResult.CatcherAnimationState);
if (HyperDashing != catchResult.CatcherHyperDash)
{
if (catchResult.CatcherHyperDash)
SetHyperDashState(2);
else
SetHyperDashState();
}
caughtObjectContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject);
droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject);
hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject);
} }
/// <summary> /// <summary>
@ -415,120 +464,104 @@ namespace osu.Game.Rulesets.Catch.UI
updateCatcher(); updateCatcher();
} }
private void placeCaughtObject(PalpableCatchHitObject source) private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position)
{ {
var caughtObject = createCaughtObject(source); var caughtObject = getCaughtObject(drawableObject.HitObject);
if (caughtObject == null) return; if (caughtObject == null) return;
caughtObject.RelativePositionAxes = Axes.None; caughtObject.CopyStateFrom(drawableObject);
caughtObject.X = source.X - X;
caughtObject.IsOnPlate = true;
caughtObject.Anchor = Anchor.TopCentre; caughtObject.Anchor = Anchor.TopCentre;
caughtObject.Origin = Anchor.Centre; caughtObject.Position = position;
caughtObject.Scale *= 0.5f; caughtObject.Scale /= 2;
caughtObject.LifetimeStart = source.StartTime;
caughtObject.LifetimeEnd = double.MaxValue;
adjustPositionInStack(caughtObject); caughtObjectContainer.Add(caughtObject);
caughtFruitContainer.Add(caughtObject);
addLighting(caughtObject);
if (!caughtObject.StaysOnPlate) if (!caughtObject.StaysOnPlate)
removeFromPlate(caughtObject, DroppedObjectAnimation.Explode); removeFromPlate(caughtObject, DroppedObjectAnimation.Explode);
} }
private void adjustPositionInStack(DrawablePalpableCatchHitObject caughtObject) private Vector2 computePositionInStack(Vector2 position, float displayRadius)
{ {
const float radius_div_2 = CatchHitObject.OBJECT_RADIUS / 2; const float radius_div_2 = CatchHitObject.OBJECT_RADIUS / 2;
const float allowance = 10; const float allowance = 10;
float caughtObjectRadius = caughtObject.DisplayRadius; while (caughtObjectContainer.Any(f => Vector2Extensions.Distance(f.Position, position) < (displayRadius + radius_div_2) / (allowance / 2)))
while (caughtFruitContainer.Any(f => Vector2Extensions.Distance(f.Position, caughtObject.Position) < (caughtObjectRadius + radius_div_2) / (allowance / 2)))
{ {
float diff = (caughtObjectRadius + radius_div_2) / allowance; float diff = (displayRadius + radius_div_2) / allowance;
caughtObject.X += (RNG.NextSingle() - 0.5f) * diff * 2; position.X += (RNG.NextSingle() - 0.5f) * diff * 2;
caughtObject.Y -= RNG.NextSingle() * diff; position.Y -= RNG.NextSingle() * diff;
} }
caughtObject.X = Math.Clamp(caughtObject.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2); position.X = Math.Clamp(position.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2);
return position;
} }
private void addLighting(DrawablePalpableCatchHitObject caughtObject) private void addLighting(CatchHitObject hitObject, float x, Color4 colour)
{ {
if (!hitLighting.Value) return;
HitExplosion hitExplosion = hitExplosionPool.Get(); HitExplosion hitExplosion = hitExplosionPool.Get();
hitExplosion.X = caughtObject.X; hitExplosion.HitObject = hitObject;
hitExplosion.Scale = new Vector2(caughtObject.HitObject.Scale); hitExplosion.X = x;
hitExplosion.ObjectColour = caughtObject.AccentColour.Value; hitExplosion.Scale = new Vector2(hitObject.Scale);
hitExplosion.ObjectColour = colour;
hitExplosionContainer.Add(hitExplosion); hitExplosionContainer.Add(hitExplosion);
} }
private DrawablePalpableCatchHitObject createCaughtObject(PalpableCatchHitObject source) private CaughtObject getCaughtObject(PalpableCatchHitObject source)
{ {
switch (source) switch (source)
{ {
case Banana banana: case Fruit _:
return new DrawableBanana(banana); return caughtFruitPool.Get();
case Fruit fruit: case Banana _:
return new DrawableFruit(fruit); return caughtBananaPool.Get();
case TinyDroplet tiny: case Droplet _:
return new DrawableTinyDroplet(tiny); return caughtDropletPool.Get();
case Droplet droplet:
return new DrawableDroplet(droplet);
default: default:
return null; return null;
} }
} }
private CaughtObject getDroppedObject(CaughtObject caughtObject)
{
var droppedObject = getCaughtObject(caughtObject.HitObject);
droppedObject.CopyStateFrom(caughtObject);
droppedObject.Anchor = Anchor.TopLeft;
droppedObject.Position = caughtObjectContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget);
return droppedObject;
}
private void clearPlate(DroppedObjectAnimation animation) private void clearPlate(DroppedObjectAnimation animation)
{ {
var caughtObjects = caughtFruitContainer.Children.ToArray(); var droppedObjects = caughtObjectContainer.Children.Select(getDroppedObject).ToArray();
caughtFruitContainer.Clear(false);
droppedObjectTarget.AddRange(caughtObjects); caughtObjectContainer.Clear(false);
foreach (var caughtObject in caughtObjects) droppedObjectTarget.AddRange(droppedObjects);
drop(caughtObject, animation);
foreach (var droppedObject in droppedObjects)
applyDropAnimation(droppedObject, animation);
} }
private void removeFromPlate(DrawablePalpableCatchHitObject caughtObject, DroppedObjectAnimation animation) private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation)
{ {
if (!caughtFruitContainer.Remove(caughtObject)) var droppedObject = getDroppedObject(caughtObject);
throw new InvalidOperationException("Can only drop a caught object on the plate");
droppedObjectTarget.Add(caughtObject); caughtObjectContainer.Remove(caughtObject);
drop(caughtObject, animation); droppedObjectTarget.Add(droppedObject);
applyDropAnimation(droppedObject, animation);
} }
private void drop(DrawablePalpableCatchHitObject d, DroppedObjectAnimation animation) private void applyDropAnimation(Drawable d, DroppedObjectAnimation animation)
{
var originalX = d.X * Scale.X;
var startTime = Clock.CurrentTime;
d.Anchor = Anchor.TopLeft;
d.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(d.DrawPosition, droppedObjectTarget);
// we cannot just apply the transforms because DHO clears transforms when state is updated
d.ApplyCustomUpdateState += (o, state) => animate(o, animation, originalX, startTime);
if (d.IsLoaded)
animate(d, animation, originalX, startTime);
}
private void animate(Drawable d, DroppedObjectAnimation animation, float originalX, double startTime)
{
using (d.BeginAbsoluteSequence(startTime))
{ {
switch (animation) switch (animation)
{ {
@ -538,6 +571,7 @@ namespace osu.Game.Rulesets.Catch.UI
break; break;
case DroppedObjectAnimation.Explode: case DroppedObjectAnimation.Explode:
var originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtObjectContainer).X * Scale.X;
d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine); d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine);
d.MoveToX(d.X + originalX * 6, 1000); d.MoveToX(d.X + originalX * 6, 1000);
d.FadeOut(750); d.FadeOut(750);
@ -546,7 +580,6 @@ namespace osu.Game.Rulesets.Catch.UI
d.Expire(); d.Expire();
} }
}
private enum DroppedObjectAnimation private enum DroppedObjectAnimation
{ {

View File

@ -5,7 +5,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
@ -22,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.UI
public readonly Catcher MovableCatcher; public readonly Catcher MovableCatcher;
private readonly CatchComboDisplay comboDisplay; private readonly CatchComboDisplay comboDisplay;
public CatcherArea(Container droppedObjectContainer, BeatmapDifficulty difficulty = null) public CatcherArea(Container<CaughtObject> droppedObjectContainer, BeatmapDifficulty difficulty = null)
{ {
Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE);
Children = new Drawable[] Children = new Drawable[]
@ -42,6 +41,8 @@ namespace osu.Game.Rulesets.Catch.UI
public void OnNewResult(DrawableCatchHitObject hitObject, JudgementResult result) public void OnNewResult(DrawableCatchHitObject hitObject, JudgementResult result)
{ {
MovableCatcher.OnNewResult(hitObject, result);
if (!result.Type.IsScorable()) if (!result.Type.IsScorable())
return; return;
@ -56,12 +57,10 @@ namespace osu.Game.Rulesets.Catch.UI
comboDisplay.OnNewResult(hitObject, result); comboDisplay.OnNewResult(hitObject, result);
} }
public void OnRevertResult(DrawableCatchHitObject fruit, JudgementResult result) public void OnRevertResult(DrawableCatchHitObject hitObject, JudgementResult result)
=> comboDisplay.OnRevertResult(fruit, result);
public bool AttemptCatch(CatchHitObject obj)
{ {
return MovableCatcher.AttemptCatch(obj); comboDisplay.OnRevertResult(hitObject, result);
MovableCatcher.OnRevertResult(hitObject, result);
} }
protected override void UpdateAfterChildren() protected override void UpdateAfterChildren()

View File

@ -6,6 +6,7 @@ using JetBrains.Annotations;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Pooling;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -20,6 +21,8 @@ namespace osu.Game.Rulesets.Catch.UI
{ {
private readonly Catcher catcher; private readonly Catcher catcher;
private readonly DrawablePool<CatcherTrailSprite> trailPool;
private readonly Container<CatcherTrailSprite> dashTrails; private readonly Container<CatcherTrailSprite> dashTrails;
private readonly Container<CatcherTrailSprite> hyperDashTrails; private readonly Container<CatcherTrailSprite> hyperDashTrails;
private readonly Container<CatcherTrailSprite> endGlowSprites; private readonly Container<CatcherTrailSprite> endGlowSprites;
@ -80,8 +83,9 @@ namespace osu.Game.Rulesets.Catch.UI
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
InternalChildren = new[] InternalChildren = new Drawable[]
{ {
trailPool = new DrawablePool<CatcherTrailSprite>(30),
dashTrails = new Container<CatcherTrailSprite> { RelativeSizeAxes = Axes.Both }, dashTrails = new Container<CatcherTrailSprite> { RelativeSizeAxes = Axes.Both },
hyperDashTrails = new Container<CatcherTrailSprite> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, hyperDashTrails = new Container<CatcherTrailSprite> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
endGlowSprites = new Container<CatcherTrailSprite> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, endGlowSprites = new Container<CatcherTrailSprite> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
@ -118,14 +122,14 @@ namespace osu.Game.Rulesets.Catch.UI
{ {
var texture = (catcher.CurrentDrawableCatcher as TextureAnimation)?.CurrentFrame ?? ((Sprite)catcher.CurrentDrawableCatcher).Texture; var texture = (catcher.CurrentDrawableCatcher as TextureAnimation)?.CurrentFrame ?? ((Sprite)catcher.CurrentDrawableCatcher).Texture;
var sprite = new CatcherTrailSprite(texture) CatcherTrailSprite sprite = trailPool.Get();
{
Anchor = catcher.Anchor, sprite.Texture = texture;
Scale = catcher.Scale, sprite.Anchor = catcher.Anchor;
Blending = BlendingParameters.Additive, sprite.Scale = catcher.Scale;
RelativePositionAxes = catcher.RelativePositionAxes, sprite.Blending = BlendingParameters.Additive;
Position = catcher.Position sprite.RelativePositionAxes = catcher.RelativePositionAxes;
}; sprite.Position = catcher.Position;
target.Add(sprite); target.Add(sprite);

View File

@ -1,22 +1,40 @@
// 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.Graphics;
using osu.Framework.Graphics.Pooling;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Catch.UI namespace osu.Game.Rulesets.Catch.UI
{ {
public class CatcherTrailSprite : Sprite public class CatcherTrailSprite : PoolableDrawable
{ {
public CatcherTrailSprite(Texture texture) public Texture Texture
{ {
Texture = texture; set => sprite.Texture = value;
}
private readonly Sprite sprite;
public CatcherTrailSprite()
{
InternalChild = sprite = new Sprite
{
RelativeSizeAxes = Axes.Both
};
Size = new Vector2(CatcherArea.CATCHER_SIZE); Size = new Vector2(CatcherArea.CATCHER_SIZE);
// Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling. // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE; OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE;
} }
protected override void FreeAfterUse()
{
ClearTransforms();
base.FreeAfterUse();
}
} }
} }

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Pooling;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Rulesets.Catch.Objects;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -15,6 +16,7 @@ namespace osu.Game.Rulesets.Catch.UI
public class HitExplosion : PoolableDrawable public class HitExplosion : PoolableDrawable
{ {
private Color4 objectColour; private Color4 objectColour;
public CatchHitObject HitObject;
public Color4 ObjectColour public Color4 ObjectColour
{ {

View File

@ -15,7 +15,7 @@ using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;

View File

@ -10,7 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Pooling;
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.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osuTK; using osuTK;

View File

@ -4,7 +4,7 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Skinning.Default;
namespace osu.Game.Rulesets.Mania.Edit.Blueprints.Components namespace osu.Game.Rulesets.Mania.Edit.Blueprints.Components
{ {

View File

@ -4,7 +4,7 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Skinning.Default;
namespace osu.Game.Rulesets.Mania.Edit.Blueprints.Components namespace osu.Game.Rulesets.Mania.Edit.Blueprints.Components
{ {

View File

@ -9,7 +9,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;

View File

@ -26,7 +26,7 @@ using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty; using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.Mania.Skinning.Legacy;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Scoring; using osu.Game.Scoring;

View File

@ -4,9 +4,9 @@
using System; using System;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Objects; 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;

View File

@ -5,7 +5,7 @@ using System.Diagnostics;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;

View File

@ -5,16 +5,17 @@ using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Layout; using osu.Framework.Layout;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Mania.Skinning.Default
{ {
/// <summary> /// <summary>
/// Represents length-wise portion of a hold note. /// Represents length-wise portion of a hold note.

View File

@ -4,7 +4,6 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -12,8 +11,9 @@ using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Mania.Skinning.Default
{ {
/// <summary> /// <summary>
/// Represents the static hit markers of notes. /// Represents the static hit markers of notes.

View File

@ -1,7 +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.
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Mania.Skinning.Default
{ {
/// <summary> /// <summary>
/// Interface for mania hold note bodies. /// Interface for mania hold note bodies.

View File

@ -9,7 +9,7 @@ using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class HitTargetInsetContainer : Container public class HitTargetInsetContainer : Container
{ {

View File

@ -14,7 +14,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyBodyPiece : LegacyManiaColumnElement public class LegacyBodyPiece : LegacyManiaColumnElement
{ {

View File

@ -12,7 +12,7 @@ using osu.Game.Skinning;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyColumnBackground : LegacyManiaColumnElement, IKeyBindingHandler<ManiaAction> public class LegacyColumnBackground : LegacyManiaColumnElement, IKeyBindingHandler<ManiaAction>
{ {

View File

@ -13,7 +13,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyHitExplosion : LegacyManiaColumnElement, IHitExplosion public class LegacyHitExplosion : LegacyManiaColumnElement, IHitExplosion
{ {

View File

@ -12,7 +12,7 @@ using osu.Game.Skinning;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyHitTarget : CompositeDrawable public class LegacyHitTarget : CompositeDrawable
{ {

View File

@ -4,7 +4,7 @@
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyHoldNoteHeadPiece : LegacyNotePiece public class LegacyHoldNoteHeadPiece : LegacyNotePiece
{ {

View File

@ -6,7 +6,7 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyHoldNoteTailPiece : LegacyNotePiece public class LegacyHoldNoteTailPiece : LegacyNotePiece
{ {

View File

@ -12,7 +12,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyKeyArea : LegacyManiaColumnElement, IKeyBindingHandler<ManiaAction> public class LegacyKeyArea : LegacyManiaColumnElement, IKeyBindingHandler<ManiaAction>
{ {

View File

@ -8,7 +8,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
/// <summary> /// <summary>
/// A <see cref="CompositeDrawable"/> which is placed somewhere within a <see cref="Column"/>. /// A <see cref="CompositeDrawable"/> which is placed somewhere within a <see cref="Column"/>.

View File

@ -12,7 +12,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyNotePiece : LegacyManiaColumnElement public class LegacyNotePiece : LegacyManiaColumnElement
{ {

View File

@ -12,7 +12,7 @@ using osu.Game.Skinning;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyStageBackground : CompositeDrawable public class LegacyStageBackground : CompositeDrawable
{ {

View File

@ -9,7 +9,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyStageForeground : CompositeDrawable public class LegacyStageForeground : CompositeDrawable
{ {

View File

@ -2,19 +2,19 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Skinning;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Skinning namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class ManiaLegacySkinTransformer : LegacySkinTransformer public class ManiaLegacySkinTransformer : LegacySkinTransformer
{ {

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osuTK.Graphics; using osuTK.Graphics;

View File

@ -10,7 +10,7 @@ using osu.Framework.Graphics.Effects;
using osu.Framework.Utils; using osu.Framework.Utils;
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.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;

View File

@ -13,7 +13,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Skinning.Default;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Tests.Mods namespace osu.Game.Rulesets.Osu.Tests.Mods

View File

@ -12,7 +12,7 @@ using osu.Framework.Graphics.OpenGL.Textures;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Testing.Input; using osu.Framework.Testing.Input;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning.Legacy;
using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;

View File

@ -12,7 +12,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using osuTK; using osuTK;

View File

@ -19,7 +19,7 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Storyboards; using osu.Game.Storyboards;
using osuTK; using osuTK;

View File

@ -12,5 +12,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
public double ApproachRate; public double ApproachRate;
public double OverallDifficulty; public double OverallDifficulty;
public int HitCircleCount; public int HitCircleCount;
public int SpinnerCount;
} }
} }

View File

@ -48,6 +48,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1); maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle); int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
int spinnerCount = beatmap.HitObjects.Count(h => h is Spinner);
return new OsuDifficultyAttributes return new OsuDifficultyAttributes
{ {
@ -59,6 +60,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
OverallDifficulty = (80 - hitWindowGreat) / 6, OverallDifficulty = (80 - hitWindowGreat) / 6,
MaxCombo = maxCombo, MaxCombo = maxCombo,
HitCircleCount = hitCirclesCount, HitCircleCount = hitCirclesCount,
SpinnerCount = spinnerCount,
Skills = skills Skills = skills
}; };
} }

View File

@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
multiplier *= 0.90; multiplier *= 0.90;
if (mods.Any(m => m is OsuModSpunOut)) if (mods.Any(m => m is OsuModSpunOut))
multiplier *= 0.95; multiplier *= 1.0 - Math.Pow((double)Attributes.SpinnerCount / totalHits, 0.85);
double aimValue = computeAimValue(); double aimValue = computeAimValue();
double speedValue = computeSpeedValue(); double speedValue = computeSpeedValue();
@ -99,16 +99,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (Attributes.MaxCombo > 0) if (Attributes.MaxCombo > 0)
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
double approachRateFactor = 1.0; double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33) if (Attributes.ApproachRate > 10.33)
approachRateFactor += 0.3 * (Attributes.ApproachRate - 10.33); approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33);
else if (Attributes.ApproachRate < 8.0) else if (Attributes.ApproachRate < 8.0)
{ approachRateFactor += 0.1 * (8.0 - Attributes.ApproachRate);
approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate);
}
aimValue *= approachRateFactor; aimValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0));
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
if (mods.Any(h => h is OsuModHidden)) if (mods.Any(h => h is OsuModHidden))
@ -137,8 +134,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0; double speedValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.SpeedStrain / 0.0675) - 4.0, 3.0) / 100000.0;
// Longer maps are worth more // Longer maps are worth more
speedValue *= 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) + double lengthBonus = 0.95 + 0.4 * Math.Min(1.0, totalHits / 2000.0) +
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0); (totalHits > 2000 ? Math.Log10(totalHits / 2000.0) * 0.5 : 0.0);
speedValue *= lengthBonus;
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
speedValue *= Math.Pow(0.97, countMiss); speedValue *= Math.Pow(0.97, countMiss);
@ -147,11 +145,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (Attributes.MaxCombo > 0) if (Attributes.MaxCombo > 0)
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0); speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
double approachRateFactor = 1.0; double approachRateFactor = 0.0;
if (Attributes.ApproachRate > 10.33) if (Attributes.ApproachRate > 10.33)
approachRateFactor += 0.3 * (Attributes.ApproachRate - 10.33); approachRateFactor += 0.4 * (Attributes.ApproachRate - 10.33);
speedValue *= approachRateFactor; speedValue *= 1.0 + Math.Min(approachRateFactor, approachRateFactor * (totalHits / 1000.0));
if (mods.Any(m => m is OsuModHidden)) if (mods.Any(m => m is OsuModHidden))
speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate); speedValue *= 1.0 + 0.04 * (12.0 - Attributes.ApproachRate);

View File

@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Skinning.Default;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Skinning.Default;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;

View File

@ -7,7 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Skinning.Default;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components

View File

@ -7,7 +7,7 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Skinning.Default;
namespace osu.Game.Rulesets.Osu.Mods namespace osu.Game.Rulesets.Osu.Mods
{ {

View File

@ -12,7 +12,7 @@ using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;

View File

@ -7,13 +7,13 @@ using JetBrains.Annotations;
using osuTK; using osuTK;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Game.Skinning; using osu.Game.Skinning;

View File

@ -9,7 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;

View File

@ -15,8 +15,8 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
using osu.Game.Skinning; using osu.Game.Skinning;

View File

@ -24,13 +24,13 @@ using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Difficulty; using osu.Game.Rulesets.Osu.Difficulty;
using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
using System; using System;
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Skinning.Legacy;
using osu.Game.Rulesets.Osu.Statistics; using osu.Game.Rulesets.Osu.Statistics;
using osu.Game.Screens.Ranking.Statistics; using osu.Game.Screens.Ranking.Statistics;

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Skinning.Default
{ {
public class ApproachCircle : Container public class ApproachCircle : Container
{ {

View File

@ -7,9 +7,10 @@ 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.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Skinning.Default
{ {
public class CirclePiece : CompositeDrawable public class CirclePiece : CompositeDrawable
{ {

View File

@ -10,10 +10,12 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Skinning.Default
{ {
public class DefaultSpinnerDisc : CompositeDrawable public class DefaultSpinnerDisc : CompositeDrawable
{ {

View File

@ -4,7 +4,7 @@
using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Lines;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Skinning.Default
{ {
public abstract class DrawableSliderPath : SmoothPath public abstract class DrawableSliderPath : SmoothPath
{ {

View File

@ -5,9 +5,10 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Skinning.Default
{ {
public class ExplodePiece : Container public class ExplodePiece : Container
{ {

Some files were not shown because too many files have changed in this diff Show More