mirror of
https://github.com/osukey/osukey.git
synced 2025-08-06 16:13:57 +09:00
Merge branch 'master' into mod-muted
This commit is contained in:
@ -52,7 +52,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.723.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.728.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
var skin = new TestSkin { FlipCatcherPlate = flip };
|
var skin = new TestSkin { FlipCatcherPlate = flip };
|
||||||
container.Child = new SkinProvidingContainer(skin)
|
container.Child = new SkinProvidingContainer(skin)
|
||||||
{
|
{
|
||||||
Child = catcher = new Catcher(new Container(), new DroppedObjectContainer())
|
Child = catcher = new Catcher(new DroppedObjectContainer())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre
|
Anchor = Anchor.Centre
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuConfigManager config { get; set; }
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
private Container trailContainer;
|
|
||||||
|
|
||||||
private DroppedObjectContainer droppedObjectContainer;
|
private DroppedObjectContainer droppedObjectContainer;
|
||||||
|
|
||||||
private TestCatcher catcher;
|
private TestCatcher catcher;
|
||||||
@ -45,7 +43,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
CircleSize = 0,
|
CircleSize = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
trailContainer = new Container();
|
|
||||||
droppedObjectContainer = new DroppedObjectContainer();
|
droppedObjectContainer = new DroppedObjectContainer();
|
||||||
|
|
||||||
Child = new Container
|
Child = new Container
|
||||||
@ -54,8 +51,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
droppedObjectContainer,
|
droppedObjectContainer,
|
||||||
catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty),
|
catcher = new TestCatcher(droppedObjectContainer, difficulty),
|
||||||
trailContainer,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -294,8 +290,8 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
public IEnumerable<CaughtObject> CaughtObjects => this.ChildrenOfType<CaughtObject>();
|
public IEnumerable<CaughtObject> CaughtObjects => this.ChildrenOfType<CaughtObject>();
|
||||||
|
|
||||||
public TestCatcher(Container trailsTarget, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty)
|
public TestCatcher(DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty)
|
||||||
: base(trailsTarget, droppedObjectTarget, difficulty)
|
: base(droppedObjectTarget, difficulty)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,10 +122,9 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
|
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
|
||||||
{
|
{
|
||||||
var droppedObjectContainer = new DroppedObjectContainer();
|
var droppedObjectContainer = new DroppedObjectContainer();
|
||||||
|
|
||||||
Add(droppedObjectContainer);
|
Add(droppedObjectContainer);
|
||||||
|
|
||||||
Catcher = new Catcher(this, droppedObjectContainer, beatmapDifficulty)
|
Catcher = new Catcher(droppedObjectContainer, beatmapDifficulty)
|
||||||
{
|
{
|
||||||
X = CatchPlayfield.CENTER_X
|
X = CatchPlayfield.CENTER_X
|
||||||
};
|
};
|
||||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCustomEndGlowColour()
|
public void TestCustomAfterImageColour()
|
||||||
{
|
{
|
||||||
var skin = new TestSkin
|
var skin = new TestSkin
|
||||||
{
|
{
|
||||||
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCustomEndGlowColourPriority()
|
public void TestCustomAfterImageColourPriority()
|
||||||
{
|
{
|
||||||
var skin = new TestSkin
|
var skin = new TestSkin
|
||||||
{
|
{
|
||||||
@ -111,39 +111,37 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
checkHyperDashFruitColour(skin, skin.HyperDashColour);
|
checkHyperDashFruitColour(skin, skin.HyperDashColour);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedEndGlowColour = null)
|
private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedAfterImageColour = null)
|
||||||
{
|
{
|
||||||
Container trailsContainer = null;
|
|
||||||
Catcher catcher = null;
|
|
||||||
CatcherTrailDisplay trails = null;
|
CatcherTrailDisplay trails = null;
|
||||||
|
Catcher catcher = null;
|
||||||
|
|
||||||
AddStep("create hyper-dashing catcher", () =>
|
AddStep("create hyper-dashing catcher", () =>
|
||||||
{
|
{
|
||||||
trailsContainer = new Container();
|
CatcherArea catcherArea;
|
||||||
Child = setupSkinHierarchy(new Container
|
Child = setupSkinHierarchy(new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Children = new Drawable[]
|
Child = catcherArea = new CatcherArea
|
||||||
{
|
{
|
||||||
catcher = new Catcher(trailsContainer, new DroppedObjectContainer())
|
Catcher = catcher = new Catcher(new DroppedObjectContainer())
|
||||||
{
|
{
|
||||||
Scale = new Vector2(4)
|
Scale = new Vector2(4)
|
||||||
},
|
}
|
||||||
trailsContainer
|
|
||||||
}
|
}
|
||||||
}, skin);
|
}, skin);
|
||||||
|
trails = catcherArea.ChildrenOfType<CatcherTrailDisplay>().Single();
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("get trails container", () =>
|
AddStep("start hyper-dash", () =>
|
||||||
{
|
{
|
||||||
trails = trailsContainer.OfType<CatcherTrailDisplay>().Single();
|
|
||||||
catcher.SetHyperDashState(2);
|
catcher.SetHyperDashState(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("catcher colour is correct", () => catcher.Colour == expectedCatcherColour);
|
AddUntilStep("catcher colour is correct", () => catcher.Colour == expectedCatcherColour);
|
||||||
|
|
||||||
AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour);
|
AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour);
|
||||||
AddAssert("catcher end-glow colours are correct", () => trails.EndGlowSpritesColour == (expectedEndGlowColour ?? expectedCatcherColour));
|
AddAssert("catcher after-image colours are correct", () => trails.HyperDashAfterImageColour == (expectedAfterImageColour ?? expectedCatcherColour));
|
||||||
|
|
||||||
AddStep("finish hyper-dashing", () =>
|
AddStep("finish hyper-dashing", () =>
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
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;
|
||||||
@ -45,14 +44,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
var trailContainer = new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.TopLeft
|
|
||||||
};
|
|
||||||
var droppedObjectContainer = new DroppedObjectContainer();
|
var droppedObjectContainer = new DroppedObjectContainer();
|
||||||
|
|
||||||
Catcher = new Catcher(trailContainer, droppedObjectContainer, difficulty)
|
Catcher = new Catcher(droppedObjectContainer, difficulty)
|
||||||
{
|
{
|
||||||
X = CENTER_X
|
X = CENTER_X
|
||||||
};
|
};
|
||||||
@ -70,7 +64,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
Catcher = Catcher,
|
Catcher = Catcher,
|
||||||
},
|
},
|
||||||
trailContainer,
|
|
||||||
HitObjectContainer,
|
HitObjectContainer,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -36,8 +36,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
public const float ALLOWED_CATCH_RANGE = 0.8f;
|
public const float ALLOWED_CATCH_RANGE = 0.8f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail
|
/// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail and after-image during a hyper-dash.
|
||||||
/// and end glow/after-image during a hyper-dash.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red;
|
public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red;
|
||||||
|
|
||||||
@ -71,11 +70,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const float caught_fruit_scale_adjust = 0.5f;
|
private const float caught_fruit_scale_adjust = 0.5f;
|
||||||
|
|
||||||
[NotNull]
|
|
||||||
private readonly Container trailsTarget;
|
|
||||||
|
|
||||||
private CatcherTrailDisplay trails;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains caught objects on the plate.
|
/// Contains caught objects on the plate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -88,30 +82,22 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public CatcherAnimationState CurrentState
|
public CatcherAnimationState CurrentState
|
||||||
{
|
{
|
||||||
get => Body.AnimationState.Value;
|
get => body.AnimationState.Value;
|
||||||
private set => Body.AnimationState.Value = value;
|
private set => body.AnimationState.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool dashing;
|
/// <summary>
|
||||||
|
/// Whether the catcher is currently dashing.
|
||||||
public bool Dashing
|
/// </summary>
|
||||||
{
|
public bool Dashing { get; set; }
|
||||||
get => dashing;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == dashing) return;
|
|
||||||
|
|
||||||
dashing = value;
|
|
||||||
|
|
||||||
updateTrailVisibility();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently facing direction.
|
/// The currently facing direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Direction VisualDirection { get; set; } = Direction.Right;
|
public Direction VisualDirection { get; set; } = Direction.Right;
|
||||||
|
|
||||||
|
public Vector2 BodyScale => Scale * body.Scale;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
|
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -122,10 +108,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly float catchWidth;
|
private readonly float catchWidth;
|
||||||
|
|
||||||
internal readonly SkinnableCatcher Body;
|
private readonly SkinnableCatcher body;
|
||||||
|
|
||||||
private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR;
|
private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR;
|
||||||
private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR;
|
|
||||||
|
|
||||||
private double hyperDashModifier = 1;
|
private double hyperDashModifier = 1;
|
||||||
private int hyperDashDirection;
|
private int hyperDashDirection;
|
||||||
@ -138,9 +123,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
private readonly DrawablePool<CaughtBanana> caughtBananaPool;
|
private readonly DrawablePool<CaughtBanana> caughtBananaPool;
|
||||||
private readonly DrawablePool<CaughtDroplet> caughtDropletPool;
|
private readonly DrawablePool<CaughtDroplet> caughtDropletPool;
|
||||||
|
|
||||||
public Catcher([NotNull] Container trailsTarget, [NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null)
|
public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null)
|
||||||
{
|
{
|
||||||
this.trailsTarget = trailsTarget;
|
|
||||||
this.droppedObjectTarget = droppedObjectTarget;
|
this.droppedObjectTarget = droppedObjectTarget;
|
||||||
|
|
||||||
Origin = Anchor.TopCentre;
|
Origin = Anchor.TopCentre;
|
||||||
@ -164,7 +148,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
// offset fruit vertically to better place "above" the plate.
|
// offset fruit vertically to better place "above" the plate.
|
||||||
Y = -5
|
Y = -5
|
||||||
},
|
},
|
||||||
Body = new SkinnableCatcher(),
|
body = new SkinnableCatcher(),
|
||||||
hitExplosionContainer = new HitExplosionContainer
|
hitExplosionContainer = new HitExplosionContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
@ -177,15 +161,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
private void load(OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
hitLighting = config.GetBindable<bool>(OsuSetting.HitLighting);
|
hitLighting = config.GetBindable<bool>(OsuSetting.HitLighting);
|
||||||
trails = new CatcherTrailDisplay(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
// don't add in above load as we may potentially modify a parent in an unsafe manner.
|
|
||||||
trailsTarget.Add(trails);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -307,10 +282,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
hyperDashTargetPosition = targetPosition;
|
hyperDashTargetPosition = targetPosition;
|
||||||
|
|
||||||
if (!wasHyperDashing)
|
if (!wasHyperDashing)
|
||||||
{
|
|
||||||
trails.DisplayEndGlow();
|
|
||||||
runHyperDashStateTransition(true);
|
runHyperDashStateTransition(true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,13 +298,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
private void runHyperDashStateTransition(bool hyperDashing)
|
private void runHyperDashStateTransition(bool hyperDashing)
|
||||||
{
|
{
|
||||||
updateTrailVisibility();
|
|
||||||
|
|
||||||
this.FadeColour(hyperDashing ? hyperDashColour : Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
|
this.FadeColour(hyperDashing ? hyperDashColour : Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing;
|
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin)
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin);
|
base.SkinChanged(skin);
|
||||||
@ -341,13 +309,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
||||||
DEFAULT_HYPER_DASH_COLOUR;
|
DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
|
||||||
hyperDashEndGlowColour =
|
|
||||||
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashAfterImage)?.Value ??
|
|
||||||
hyperDashColour;
|
|
||||||
|
|
||||||
trails.HyperDashTrailsColour = hyperDashColour;
|
|
||||||
trails.EndGlowSpritesColour = hyperDashEndGlowColour;
|
|
||||||
|
|
||||||
flipCatcherPlate = skin.GetConfig<CatchSkinConfiguration, bool>(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true;
|
flipCatcherPlate = skin.GetConfig<CatchSkinConfiguration, bool>(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true;
|
||||||
|
|
||||||
runHyperDashStateTransition(HyperDashing);
|
runHyperDashStateTransition(HyperDashing);
|
||||||
@ -358,7 +319,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
var scaleFromDirection = new Vector2((int)VisualDirection, 1);
|
var scaleFromDirection = new Vector2((int)VisualDirection, 1);
|
||||||
Body.Scale = scaleFromDirection;
|
body.Scale = scaleFromDirection;
|
||||||
caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
|
caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
|
||||||
|
|
||||||
// Correct overshooting.
|
// Correct overshooting.
|
||||||
|
@ -25,17 +25,15 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
public Catcher Catcher
|
public Catcher Catcher
|
||||||
{
|
{
|
||||||
get => catcher;
|
get => catcher;
|
||||||
set
|
set => catcherContainer.Child = catcher = value;
|
||||||
{
|
|
||||||
if (catcher != null)
|
|
||||||
Remove(catcher);
|
|
||||||
|
|
||||||
Add(catcher = value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly Container<Catcher> catcherContainer;
|
||||||
|
|
||||||
private readonly CatchComboDisplay comboDisplay;
|
private readonly CatchComboDisplay comboDisplay;
|
||||||
|
|
||||||
|
private readonly CatcherTrailDisplay catcherTrails;
|
||||||
|
|
||||||
private Catcher catcher;
|
private Catcher catcher;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -45,20 +43,28 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private int currentDirection;
|
private int currentDirection;
|
||||||
|
|
||||||
|
// TODO: support replay rewind
|
||||||
|
private bool lastHyperDashState;
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <see cref="Catcher"/> must be set before loading.
|
/// <see cref="Catcher"/> must be set before loading.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public CatcherArea()
|
public CatcherArea()
|
||||||
{
|
{
|
||||||
Size = new Vector2(CatchPlayfield.WIDTH, Catcher.BASE_SIZE);
|
Size = new Vector2(CatchPlayfield.WIDTH, Catcher.BASE_SIZE);
|
||||||
Child = comboDisplay = new CatchComboDisplay
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.None,
|
catcherContainer = new Container<Catcher> { RelativeSizeAxes = Axes.Both },
|
||||||
AutoSizeAxes = Axes.Both,
|
catcherTrails = new CatcherTrailDisplay(),
|
||||||
Anchor = Anchor.TopLeft,
|
comboDisplay = new CatchComboDisplay
|
||||||
Origin = Anchor.Centre,
|
{
|
||||||
Margin = new MarginPadding { Bottom = 350f },
|
RelativeSizeAxes = Axes.None,
|
||||||
X = CatchPlayfield.CENTER_X
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.TopLeft,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Margin = new MarginPadding { Bottom = 350f },
|
||||||
|
X = CatchPlayfield.CENTER_X
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +108,19 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
base.UpdateAfterChildren();
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
comboDisplay.X = Catcher.X;
|
comboDisplay.X = Catcher.X;
|
||||||
|
|
||||||
|
if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0)
|
||||||
|
catcherTrails.DisplayHyperDashAfterImage(Catcher.CurrentState, Catcher.X, Catcher.BodyScale);
|
||||||
|
|
||||||
|
if (Catcher.Dashing || Catcher.HyperDashing)
|
||||||
|
{
|
||||||
|
double generationInterval = Catcher.HyperDashing ? 25 : 50;
|
||||||
|
|
||||||
|
if (Time.Current - catcherTrails.LastDashTrailTime >= generationInterval)
|
||||||
|
catcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastHyperDashState = Catcher.HyperDashing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCatcherPosition(float X)
|
public void SetCatcherPosition(float X)
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
// 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 JetBrains.Annotations;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Pooling;
|
using osu.Framework.Graphics.Pooling;
|
||||||
|
using osu.Game.Rulesets.Catch.Skinning;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -15,70 +16,27 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// Represents a component responsible for displaying
|
/// Represents a component responsible for displaying
|
||||||
/// the appropriate catcher trails when requested to.
|
/// the appropriate catcher trails when requested to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CatcherTrailDisplay : CompositeDrawable
|
public class CatcherTrailDisplay : SkinReloadableDrawable
|
||||||
{
|
{
|
||||||
private readonly Catcher catcher;
|
/// <summary>
|
||||||
|
/// The most recent time a dash trail was added to this container.
|
||||||
|
/// Only alive (not faded out) trails are considered.
|
||||||
|
/// Returns <see cref="double.NegativeInfinity"/> if no dash trail is alive.
|
||||||
|
/// </summary>
|
||||||
|
public double LastDashTrailTime => getLastDashTrailTime();
|
||||||
|
|
||||||
|
public Color4 HyperDashTrailsColour => hyperDashTrails.Colour;
|
||||||
|
|
||||||
|
public Color4 HyperDashAfterImageColour => hyperDashAfterImages.Colour;
|
||||||
|
|
||||||
private readonly DrawablePool<CatcherTrail> trailPool;
|
private readonly DrawablePool<CatcherTrail> trailPool;
|
||||||
|
|
||||||
private readonly Container<CatcherTrail> dashTrails;
|
private readonly Container<CatcherTrail> dashTrails;
|
||||||
private readonly Container<CatcherTrail> hyperDashTrails;
|
private readonly Container<CatcherTrail> hyperDashTrails;
|
||||||
private readonly Container<CatcherTrail> endGlowSprites;
|
private readonly Container<CatcherTrail> hyperDashAfterImages;
|
||||||
|
|
||||||
private Color4 hyperDashTrailsColour = Catcher.DEFAULT_HYPER_DASH_COLOUR;
|
public CatcherTrailDisplay()
|
||||||
|
|
||||||
public Color4 HyperDashTrailsColour
|
|
||||||
{
|
{
|
||||||
get => hyperDashTrailsColour;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (hyperDashTrailsColour == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
hyperDashTrailsColour = value;
|
|
||||||
hyperDashTrails.Colour = hyperDashTrailsColour;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Color4 endGlowSpritesColour = Catcher.DEFAULT_HYPER_DASH_COLOUR;
|
|
||||||
|
|
||||||
public Color4 EndGlowSpritesColour
|
|
||||||
{
|
|
||||||
get => endGlowSpritesColour;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (endGlowSpritesColour == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
endGlowSpritesColour = value;
|
|
||||||
endGlowSprites.Colour = endGlowSpritesColour;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool trail;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether to start displaying trails following the catcher.
|
|
||||||
/// </summary>
|
|
||||||
public bool DisplayTrail
|
|
||||||
{
|
|
||||||
get => trail;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (trail == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
trail = value;
|
|
||||||
|
|
||||||
if (trail)
|
|
||||||
displayTrail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CatcherTrailDisplay([NotNull] Catcher catcher)
|
|
||||||
{
|
|
||||||
this.catcher = catcher ?? throw new ArgumentNullException(nameof(catcher));
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
@ -86,47 +44,68 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
trailPool = new DrawablePool<CatcherTrail>(30),
|
trailPool = new DrawablePool<CatcherTrail>(30),
|
||||||
dashTrails = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both },
|
dashTrails = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both },
|
||||||
hyperDashTrails = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
|
hyperDashTrails = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
|
||||||
endGlowSprites = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
|
hyperDashAfterImages = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void SkinChanged(ISkinSource skin)
|
||||||
|
{
|
||||||
|
base.SkinChanged(skin);
|
||||||
|
|
||||||
|
hyperDashTrails.Colour = skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ?? Catcher.DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
hyperDashAfterImages.Colour = skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashTrails.Colour;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Displays a single end-glow catcher sprite.
|
/// Displays a hyper-dash after-image of the catcher.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void DisplayEndGlow()
|
public void DisplayHyperDashAfterImage(CatcherAnimationState animationState, float x, Vector2 scale)
|
||||||
{
|
{
|
||||||
var endGlow = createTrailSprite(endGlowSprites);
|
var trail = createTrail(animationState, x, scale);
|
||||||
|
|
||||||
endGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In);
|
hyperDashAfterImages.Add(trail);
|
||||||
endGlow.ScaleTo(endGlow.Scale * 0.95f).ScaleTo(endGlow.Scale * 1.2f, 1200, Easing.In);
|
|
||||||
endGlow.FadeOut(1200);
|
trail.MoveToOffset(new Vector2(0, -10), 1200, Easing.In);
|
||||||
endGlow.Expire(true);
|
trail.ScaleTo(trail.Scale * 0.95f).ScaleTo(trail.Scale * 1.2f, 1200, Easing.In);
|
||||||
|
trail.FadeOut(1200);
|
||||||
|
trail.Expire(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayTrail()
|
public void DisplayDashTrail(CatcherAnimationState animationState, float x, Vector2 scale, bool hyperDashing)
|
||||||
{
|
{
|
||||||
if (!DisplayTrail)
|
var trail = createTrail(animationState, x, scale);
|
||||||
return;
|
|
||||||
|
|
||||||
var sprite = createTrailSprite(catcher.HyperDashing ? hyperDashTrails : dashTrails);
|
if (hyperDashing)
|
||||||
|
hyperDashTrails.Add(trail);
|
||||||
|
else
|
||||||
|
dashTrails.Add(trail);
|
||||||
|
|
||||||
sprite.FadeTo(0.4f).FadeOut(800, Easing.OutQuint);
|
trail.FadeTo(0.4f).FadeOut(800, Easing.OutQuint);
|
||||||
sprite.Expire(true);
|
trail.Expire(true);
|
||||||
|
|
||||||
Scheduler.AddDelayed(displayTrail, catcher.HyperDashing ? 25 : 50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CatcherTrail createTrailSprite(Container<CatcherTrail> target)
|
private CatcherTrail createTrail(CatcherAnimationState animationState, float x, Vector2 scale)
|
||||||
{
|
{
|
||||||
CatcherTrail sprite = trailPool.Get();
|
CatcherTrail trail = trailPool.Get();
|
||||||
|
|
||||||
sprite.AnimationState = catcher.CurrentState;
|
trail.AnimationState = animationState;
|
||||||
sprite.Scale = catcher.Scale * catcher.Body.Scale;
|
trail.Scale = scale;
|
||||||
sprite.Position = catcher.Position;
|
trail.Position = new Vector2(x, 0);
|
||||||
|
|
||||||
target.Add(sprite);
|
return trail;
|
||||||
|
}
|
||||||
|
|
||||||
return sprite;
|
private double getLastDashTrailTime()
|
||||||
|
{
|
||||||
|
double maxTime = double.NegativeInfinity;
|
||||||
|
|
||||||
|
foreach (var trail in dashTrails)
|
||||||
|
maxTime = Math.Max(maxTime, trail.LifetimeStart);
|
||||||
|
|
||||||
|
foreach (var trail in hyperDashTrails)
|
||||||
|
maxTime = Math.Max(maxTime, trail.LifetimeStart);
|
||||||
|
|
||||||
|
return maxTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,9 @@ using osu.Game.Rulesets.Mania.Beatmaps;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModMirror : Mod, IApplicableToBeatmap
|
public class ManiaModMirror : ModMirror, IApplicableToBeatmap
|
||||||
{
|
{
|
||||||
public override string Name => "Mirror";
|
|
||||||
public override string Acronym => "MR";
|
|
||||||
public override ModType Type => ModType.Conversion;
|
|
||||||
public override string Description => "Notes are flipped horizontally.";
|
public override string Description => "Notes are flipped horizontally.";
|
||||||
public override double ScoreMultiplier => 1;
|
|
||||||
|
|
||||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.Utils;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
@ -15,23 +14,13 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => 1.06;
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModMirror)).ToArray();
|
||||||
|
|
||||||
public void ApplyToHitObject(HitObject hitObject)
|
public void ApplyToHitObject(HitObject hitObject)
|
||||||
{
|
{
|
||||||
var osuObject = (OsuHitObject)hitObject;
|
var osuObject = (OsuHitObject)hitObject;
|
||||||
|
|
||||||
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y);
|
OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
|
||||||
|
|
||||||
if (!(hitObject is Slider slider))
|
|
||||||
return;
|
|
||||||
|
|
||||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
|
||||||
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
|
||||||
|
|
||||||
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
|
|
||||||
foreach (var point in controlPoints)
|
|
||||||
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);
|
|
||||||
|
|
||||||
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
50
osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs
Normal file
50
osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
public class OsuModMirror : ModMirror, IApplicableToHitObject
|
||||||
|
{
|
||||||
|
public override string Description => "Flip objects on the chosen axes.";
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) };
|
||||||
|
|
||||||
|
[SettingSource("Mirrored axes", "Choose which axes objects are mirrored over.")]
|
||||||
|
public Bindable<MirrorType> Reflection { get; } = new Bindable<MirrorType>();
|
||||||
|
|
||||||
|
public void ApplyToHitObject(HitObject hitObject)
|
||||||
|
{
|
||||||
|
var osuObject = (OsuHitObject)hitObject;
|
||||||
|
|
||||||
|
switch (Reflection.Value)
|
||||||
|
{
|
||||||
|
case MirrorType.Horizontal:
|
||||||
|
OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MirrorType.Vertical:
|
||||||
|
OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MirrorType.Both:
|
||||||
|
OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject);
|
||||||
|
OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MirrorType
|
||||||
|
{
|
||||||
|
Horizontal,
|
||||||
|
Vertical,
|
||||||
|
Both
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Audio.Track;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
@ -16,7 +14,6 @@ using osu.Game.Beatmaps.ControlPoints;
|
|||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -29,7 +26,6 @@ using osu.Game.Rulesets.Osu.UI;
|
|||||||
using osu.Game.Rulesets.Osu.Utils;
|
using osu.Game.Rulesets.Osu.Utils;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Skinning;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -67,11 +63,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const float distance_cap = 380f;
|
private const float distance_cap = 380f;
|
||||||
|
|
||||||
// The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle.
|
|
||||||
// The closer the hit objects draw to the border, the sharper the turn
|
|
||||||
private const byte border_distance_x = 192;
|
|
||||||
private const byte border_distance_y = 144;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The extent of rotation towards playfield centre when a circle is near the edge
|
/// The extent of rotation towards playfield centre when a circle is near the edge
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -341,46 +332,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
drawableRuleset.Overlays.Add(new TargetBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime));
|
drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime));
|
||||||
}
|
|
||||||
|
|
||||||
public class TargetBeatContainer : BeatSyncedContainer
|
|
||||||
{
|
|
||||||
private readonly double firstHitTime;
|
|
||||||
|
|
||||||
private PausableSkinnableSound sample;
|
|
||||||
|
|
||||||
public TargetBeatContainer(double firstHitTime)
|
|
||||||
{
|
|
||||||
this.firstHitTime = firstHitTime;
|
|
||||||
AllowMistimedEventFiring = false;
|
|
||||||
Divisor = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana"))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
|
||||||
{
|
|
||||||
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
|
|
||||||
|
|
||||||
if (!IsBeatSyncedWithTrack) return;
|
|
||||||
|
|
||||||
int timeSignature = (int)timingPoint.TimeSignature;
|
|
||||||
|
|
||||||
// play metronome from one measure before the first object.
|
|
||||||
if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature)
|
|
||||||
return;
|
|
||||||
|
|
||||||
sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f;
|
|
||||||
sample.Play();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -166,6 +166,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
new OsuModDifficultyAdjust(),
|
new OsuModDifficultyAdjust(),
|
||||||
new OsuModClassic(),
|
new OsuModClassic(),
|
||||||
new OsuModRandom(),
|
new OsuModRandom(),
|
||||||
|
new OsuModMirror(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Utils
|
namespace osu.Game.Rulesets.Osu.Utils
|
||||||
@ -100,5 +104,47 @@ namespace osu.Game.Rulesets.Osu.Utils
|
|||||||
initial.Length * MathF.Sin(finalAngleRad)
|
initial.Length * MathF.Sin(finalAngleRad)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reflects the position of the <see cref="OsuHitObject"/> in the playfield horizontally.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="osuObject">The object to reflect.</param>
|
||||||
|
public static void ReflectHorizontally(OsuHitObject osuObject)
|
||||||
|
{
|
||||||
|
osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y);
|
||||||
|
|
||||||
|
if (!(osuObject is Slider slider))
|
||||||
|
return;
|
||||||
|
|
||||||
|
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
||||||
|
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
|
||||||
|
|
||||||
|
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
|
||||||
|
foreach (var point in controlPoints)
|
||||||
|
point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y);
|
||||||
|
|
||||||
|
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reflects the position of the <see cref="OsuHitObject"/> in the playfield vertically.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="osuObject">The object to reflect.</param>
|
||||||
|
public static void ReflectVertically(OsuHitObject osuObject)
|
||||||
|
{
|
||||||
|
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y);
|
||||||
|
|
||||||
|
if (!(osuObject is Slider slider))
|
||||||
|
return;
|
||||||
|
|
||||||
|
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
|
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
|
|
||||||
|
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
|
||||||
|
foreach (var point in controlPoints)
|
||||||
|
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);
|
||||||
|
|
||||||
|
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,19 +171,24 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
WebRequest?.Abort();
|
WebRequest?.Abort();
|
||||||
|
|
||||||
string responseString = WebRequest?.GetResponseString();
|
// in the case of a cancellation we don't care about whether there's an error in the response.
|
||||||
|
if (!(e is OperationCanceledException))
|
||||||
if (!string.IsNullOrEmpty(responseString))
|
|
||||||
{
|
{
|
||||||
try
|
string responseString = WebRequest?.GetResponseString();
|
||||||
{
|
|
||||||
// attempt to decode a displayable error string.
|
// naive check whether there's an error in the response to avoid unnecessary JSON deserialisation.
|
||||||
var error = JsonConvert.DeserializeObject<DisplayableError>(responseString);
|
if (!string.IsNullOrEmpty(responseString) && responseString.Contains(@"""error"""))
|
||||||
if (error != null)
|
|
||||||
e = new APIException(error.ErrorMessage, e);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// attempt to decode a displayable error string.
|
||||||
|
var error = JsonConvert.DeserializeObject<DisplayableError>(responseString);
|
||||||
|
if (error != null)
|
||||||
|
e = new APIException(error.ErrorMessage, e);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,10 +116,7 @@ namespace osu.Game.Online
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Log($"{clientName} connection error: {e}", LoggingTarget.Network);
|
await handleErrorAndDelay(e, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// retry on any failure.
|
|
||||||
await Task.Delay(5000, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,6 +126,15 @@ namespace osu.Game.Online
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles an exception and delays an async flow.
|
||||||
|
/// </summary>
|
||||||
|
private async Task handleErrorAndDelay(Exception exception, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Logger.Log($"{clientName} connection error: {exception}", LoggingTarget.Network);
|
||||||
|
await Task.Delay(5000, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
private HubConnection buildConnection(CancellationToken cancellationToken)
|
private HubConnection buildConnection(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var builder = new HubConnectionBuilder()
|
var builder = new HubConnectionBuilder()
|
||||||
@ -155,17 +161,18 @@ namespace osu.Game.Online
|
|||||||
return newConnection;
|
return newConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken)
|
private async Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
isConnected.Value = false;
|
isConnected.Value = false;
|
||||||
|
|
||||||
Logger.Log(ex != null ? $"{clientName} lost connection: {ex}" : $"{clientName} disconnected", LoggingTarget.Network);
|
if (ex != null)
|
||||||
|
await handleErrorAndDelay(ex, cancellationToken).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
Logger.Log($"{clientName} disconnected", LoggingTarget.Network);
|
||||||
|
|
||||||
// make sure a disconnect wasn't triggered (and this is still the active connection).
|
// make sure a disconnect wasn't triggered (and this is still the active connection).
|
||||||
if (!cancellationToken.IsCancellationRequested)
|
if (!cancellationToken.IsCancellationRequested)
|
||||||
Task.Run(connect, default);
|
await Task.Run(connect, default).ConfigureAwait(false);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task disconnect(bool takeLock)
|
private async Task disconnect(bool takeLock)
|
||||||
|
@ -18,6 +18,8 @@ using osu.Game.Scoring;
|
|||||||
using osu.Game.Users.Drawables;
|
using osu.Game.Users.Drawables;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.BeatmapSet.Scores
|
namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||||
{
|
{
|
||||||
@ -211,11 +213,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
return content.ToArray();
|
return content.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty);
|
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? default);
|
||||||
|
|
||||||
private class HeaderText : OsuSpriteText
|
private class HeaderText : OsuSpriteText
|
||||||
{
|
{
|
||||||
public HeaderText(string text)
|
public HeaderText(LocalisableString text)
|
||||||
{
|
{
|
||||||
Text = text.ToUpper();
|
Text = text.ToUpper();
|
||||||
Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold);
|
Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold);
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
|
|||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osu.Game.Users.Drawables;
|
using osu.Game.Users.Drawables;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Rankings.Tables
|
namespace osu.Game.Overlays.Rankings.Tables
|
||||||
{
|
{
|
||||||
@ -74,7 +75,7 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
|
|
||||||
protected override Drawable CreateHeader(int index, TableColumn column)
|
protected override Drawable CreateHeader(int index, TableColumn column)
|
||||||
{
|
{
|
||||||
var title = column?.Header ?? string.Empty;
|
var title = column?.Header ?? default;
|
||||||
return new HeaderText(title, title == HighlightedColumn);
|
return new HeaderText(title, title == HighlightedColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
{
|
{
|
||||||
private readonly bool isHighlighted;
|
private readonly bool isHighlighted;
|
||||||
|
|
||||||
public HeaderText(string text, bool isHighlighted)
|
public HeaderText(LocalisableString text, bool isHighlighted)
|
||||||
{
|
{
|
||||||
this.isHighlighted = isHighlighted;
|
this.isHighlighted = isHighlighted;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Rankings.Tables
|
namespace osu.Game.Overlays.Rankings.Tables
|
||||||
{
|
{
|
||||||
@ -31,8 +32,8 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
|
|
||||||
protected override Drawable CreateHeader(int index, TableColumn column)
|
protected override Drawable CreateHeader(int index, TableColumn column)
|
||||||
{
|
{
|
||||||
var title = column?.Header ?? string.Empty;
|
var title = column?.Header ?? default;
|
||||||
return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title));
|
return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override Country GetCountry(UserStatistics item) => item.User.Country;
|
protected sealed override Country GetCountry(UserStatistics item) => item.User.Country;
|
||||||
@ -66,7 +67,7 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
|
|
||||||
private class UserTableHeaderText : HeaderText
|
private class UserTableHeaderText : HeaderText
|
||||||
{
|
{
|
||||||
public UserTableHeaderText(string text, bool isHighlighted, bool isGrade)
|
public UserTableHeaderText(LocalisableString text, bool isHighlighted, bool isGrade)
|
||||||
: base(text, isHighlighted)
|
: base(text, isHighlighted)
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding
|
Margin = new MarginPadding
|
||||||
|
53
osu.Game/Rulesets/Mods/Metronome.cs
Normal file
53
osu.Game/Rulesets/Mods/Metronome.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// 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.Audio.Track;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mods
|
||||||
|
{
|
||||||
|
public class Metronome : BeatSyncedContainer
|
||||||
|
{
|
||||||
|
private readonly double firstHitTime;
|
||||||
|
|
||||||
|
private PausableSkinnableSound sample;
|
||||||
|
|
||||||
|
/// <param name="firstHitTime">Start time of the first hit object, used for providing a count down.</param>
|
||||||
|
public Metronome(double firstHitTime)
|
||||||
|
{
|
||||||
|
this.firstHitTime = firstHitTime;
|
||||||
|
AllowMistimedEventFiring = false;
|
||||||
|
Divisor = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana"))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||||
|
{
|
||||||
|
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
|
||||||
|
|
||||||
|
if (!IsBeatSyncedWithTrack) return;
|
||||||
|
|
||||||
|
int timeSignature = (int)timingPoint.TimeSignature;
|
||||||
|
|
||||||
|
// play metronome from one measure before the first object.
|
||||||
|
if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f;
|
||||||
|
sample.Play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
osu.Game/Rulesets/Mods/ModMirror.cs
Normal file
13
osu.Game/Rulesets/Mods/ModMirror.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mods
|
||||||
|
{
|
||||||
|
public abstract class ModMirror : Mod
|
||||||
|
{
|
||||||
|
public override string Name => "Mirror";
|
||||||
|
public override string Acronym => "MR";
|
||||||
|
public override ModType Type => ModType.Conversion;
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,12 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
@ -42,11 +44,11 @@ namespace osu.Game.Screens.Edit
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty);
|
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? default);
|
||||||
|
|
||||||
private class HeaderText : OsuSpriteText
|
private class HeaderText : OsuSpriteText
|
||||||
{
|
{
|
||||||
public HeaderText(string text)
|
public HeaderText(LocalisableString text)
|
||||||
{
|
{
|
||||||
Text = text.ToUpper();
|
Text = text.ToUpper();
|
||||||
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold);
|
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold);
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.3.0" />
|
<PackageReference Include="Realm" Version="10.3.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.723.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.728.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.8.2" />
|
<PackageReference Include="Sentry" Version="3.8.2" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.723.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.728.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
@ -93,7 +93,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.723.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.728.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
Reference in New Issue
Block a user