-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add repel mod to the osu ruleset #18607
Changes from 16 commits
10287e0
5d83862
b7bdad4
e5171aa
f21c9fb
f54a68f
6e883a6
4e01db0
569c399
2fe34f1
a17e181
0281bf6
40e98f8
d5b4d14
b3f23d9
62beae4
36f129a
a606d54
6443338
54fe843
28278e2
3d9252e
fa626a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. | ||
// See the LICENCE file in the repository root for full licence text. | ||
|
||
using NUnit.Framework; | ||
using osu.Game.Rulesets.Osu.Mods; | ||
|
||
namespace osu.Game.Rulesets.Osu.Tests.Mods | ||
{ | ||
public class TestSceneOsuModRepel : OsuModTestScene | ||
{ | ||
[TestCase(0.1f)] | ||
[TestCase(0.5f)] | ||
[TestCase(1)] | ||
public void TestRepel(float strength) | ||
{ | ||
CreateModTest(new ModTestData | ||
{ | ||
Mod = new OsuModRepel | ||
{ | ||
RepulsionStrength = { Value = strength }, | ||
}, | ||
PassCondition = () => true, | ||
Autoplay = false, | ||
}); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// 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. | ||
|
||
#nullable disable | ||
|
||
using System; | ||
using osu.Framework.Bindables; | ||
using osu.Framework.Utils; | ||
using osu.Game.Configuration; | ||
using osu.Game.Rulesets.Mods; | ||
using osu.Game.Rulesets.Objects.Drawables; | ||
using osu.Game.Rulesets.Osu.Objects; | ||
using osu.Game.Rulesets.Osu.Objects.Drawables; | ||
using osu.Game.Rulesets.Osu.UI; | ||
using osu.Game.Rulesets.Osu.Utils; | ||
using osu.Game.Rulesets.UI; | ||
using osuTK; | ||
|
||
namespace osu.Game.Rulesets.Osu.Mods | ||
{ | ||
internal class OsuModRepel : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject> | ||
{ | ||
public override string Name => "Repel"; | ||
public override string Acronym => "RP"; | ||
public override ModType Type => ModType.Fun; | ||
public override string Description => "Hit objects run away!"; | ||
public override double ScoreMultiplier => 1; | ||
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised) }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This mod is declaring incompatibility with 5 others, but 4 of those 5 are not declaring incompatibility with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You still missed autoplay.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and relax. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
private IFrameStableClock gameplayClock; | ||
|
||
[SettingSource("Repulsion strength", "How strong the repulsion is.", 0)] | ||
public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f) | ||
{ | ||
Precision = 0.05f, | ||
MinValue = 0.05f, | ||
MaxValue = 1.0f, | ||
}; | ||
|
||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset) | ||
{ | ||
gameplayClock = drawableRuleset.FrameStableClock; | ||
|
||
// Hide judgment displays and follow points as they won't make any sense. | ||
// Judgements can potentially be turned on in a future where they display at a position relative to their drawable counterpart. | ||
drawableRuleset.Playfield.DisplayJudgements.Value = false; | ||
(drawableRuleset.Playfield as OsuPlayfield)?.FollowPoints.Hide(); | ||
} | ||
|
||
public void Update(Playfield playfield) | ||
{ | ||
var cursorPos = playfield.Cursor.ActiveCursor.DrawPosition; | ||
|
||
foreach (var drawable in playfield.HitObjectContainer.AliveObjects) | ||
{ | ||
var destination = Vector2.Clamp(2 * drawable.Position - cursorPos, Vector2.Zero, OsuPlayfield.BASE_SIZE); | ||
|
||
if (drawable.HitObject is Slider thisSlider) | ||
{ | ||
var possibleMovementBounds = OsuHitObjectGenerationUtils.CalculatePossibleMovementBounds(thisSlider); | ||
|
||
destination = Vector2.Clamp( | ||
destination, | ||
new Vector2(possibleMovementBounds.Left, possibleMovementBounds.Top), | ||
new Vector2(possibleMovementBounds.Right, possibleMovementBounds.Bottom) | ||
); | ||
} | ||
|
||
switch (drawable) | ||
{ | ||
case DrawableHitCircle circle: | ||
easeTo(circle, destination); | ||
break; | ||
|
||
case DrawableSlider slider: | ||
|
||
if (!slider.HeadCircle.Result.HasResult) | ||
easeTo(slider, destination); | ||
else | ||
easeTo(slider, destination - slider.Ball.DrawPosition); | ||
|
||
break; | ||
} | ||
} | ||
} | ||
|
||
private void easeTo(DrawableHitObject hitObject, Vector2 destination) | ||
{ | ||
double dampLength = Vector2.Distance(hitObject.Position, destination) / (0.04 * RepulsionStrength.Value + 0.04); | ||
|
||
float x = (float)Interpolation.DampContinuously(hitObject.X, destination.X, dampLength, gameplayClock.ElapsedFrameTime); | ||
float y = (float)Interpolation.DampContinuously(hitObject.Y, destination.Y, dampLength, gameplayClock.ElapsedFrameTime); | ||
|
||
hitObject.Position = new Vector2(x, y); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be preferable to have all new code have NRT enabled. The problem here is going to be
gameplayClock
, but I think the way to go here is to annotate it as nullable and then add aDebug.Assert(gameplayClock != null)
ineaseTo()
.