mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 17:32:54 +08:00
Merge branch 'master' into fix-editor-player-exit-crash
This commit is contained in:
commit
bde34380a4
@ -52,7 +52,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.707.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.713.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. -->
|
||||||
|
175
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs
Normal file
175
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||||
|
{
|
||||||
|
public class TestSceneOsuModSingleTap : OsuModTestScene
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestInputSingular() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModSingleTap(),
|
||||||
|
PassCondition = () => Player.ScoreProcessor.Combo.Value == 2,
|
||||||
|
Autoplay = false,
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject>
|
||||||
|
{
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 500,
|
||||||
|
Position = new Vector2(100),
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 1000,
|
||||||
|
Position = new Vector2(200, 100),
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 1500,
|
||||||
|
Position = new Vector2(300, 100),
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 2000,
|
||||||
|
Position = new Vector2(400, 100),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ReplayFrames = new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton),
|
||||||
|
new OsuReplayFrame(501, new Vector2(100)),
|
||||||
|
new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.LeftButton),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestInputAlternating() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModSingleTap(),
|
||||||
|
PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 1,
|
||||||
|
Autoplay = false,
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject>
|
||||||
|
{
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 500,
|
||||||
|
Position = new Vector2(100),
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 1000,
|
||||||
|
Position = new Vector2(200, 100),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ReplayFrames = new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton),
|
||||||
|
new OsuReplayFrame(501, new Vector2(100)),
|
||||||
|
new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.RightButton),
|
||||||
|
new OsuReplayFrame(1001, new Vector2(200, 100)),
|
||||||
|
new OsuReplayFrame(1500, new Vector2(300, 100), OsuAction.LeftButton),
|
||||||
|
new OsuReplayFrame(1501, new Vector2(300, 100)),
|
||||||
|
new OsuReplayFrame(2000, new Vector2(400, 100), OsuAction.RightButton),
|
||||||
|
new OsuReplayFrame(2001, new Vector2(400, 100)),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures singletapping is reset before the first hitobject after intro.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestInputAlternatingAtIntro() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModSingleTap(),
|
||||||
|
PassCondition = () => Player.ScoreProcessor.Combo.Value == 1,
|
||||||
|
Autoplay = false,
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject>
|
||||||
|
{
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 1000,
|
||||||
|
Position = new Vector2(100),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ReplayFrames = new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
// first press during intro.
|
||||||
|
new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton),
|
||||||
|
new OsuReplayFrame(501, new Vector2(200)),
|
||||||
|
// press different key at hitobject and ensure it has been hit.
|
||||||
|
new OsuReplayFrame(1000, new Vector2(100), OsuAction.RightButton),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures singletapping is reset before the first hitobject after a break.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestInputAlternatingWithBreak() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModSingleTap(),
|
||||||
|
PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 2,
|
||||||
|
Autoplay = false,
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
Breaks = new List<BreakPeriod>
|
||||||
|
{
|
||||||
|
new BreakPeriod(500, 2000),
|
||||||
|
},
|
||||||
|
HitObjects = new List<HitObject>
|
||||||
|
{
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 500,
|
||||||
|
Position = new Vector2(100),
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 2500,
|
||||||
|
Position = new Vector2(500, 100),
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 3000,
|
||||||
|
Position = new Vector2(500, 100),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ReplayFrames = new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
// first press to start singletap lock.
|
||||||
|
new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton),
|
||||||
|
new OsuReplayFrame(501, new Vector2(100)),
|
||||||
|
// press different key after break but before hit object.
|
||||||
|
new OsuReplayFrame(2250, new Vector2(300, 100), OsuAction.RightButton),
|
||||||
|
new OsuReplayFrame(2251, new Vector2(300, 100)),
|
||||||
|
// press same key at second hitobject and ensure it has been hit.
|
||||||
|
new OsuReplayFrame(2500, new Vector2(500, 100), OsuAction.LeftButton),
|
||||||
|
new OsuReplayFrame(2501, new Vector2(500, 100)),
|
||||||
|
// press different key at third hitobject and ensure it has been missed.
|
||||||
|
new OsuReplayFrame(3000, new Vector2(500, 100), OsuAction.RightButton),
|
||||||
|
new OsuReplayFrame(3001, new Vector2(500, 100)),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
114
osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs
Normal file
114
osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// 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 System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
public abstract class InputBlockingMod : Mod, IApplicableToDrawableRuleset<OsuHitObject>
|
||||||
|
{
|
||||||
|
public override double ScoreMultiplier => 1.0;
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax), typeof(OsuModCinema) };
|
||||||
|
public override ModType Type => ModType.Conversion;
|
||||||
|
|
||||||
|
private const double flash_duration = 1000;
|
||||||
|
|
||||||
|
private DrawableRuleset<OsuHitObject> ruleset = null!;
|
||||||
|
|
||||||
|
protected OsuAction? LastAcceptedAction { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is different from <see cref="Player.IsBreakTime"/> in that the periods here end strictly at the first object after the break, rather than the break's end time.
|
||||||
|
/// </remarks>
|
||||||
|
private PeriodTracker nonGameplayPeriods = null!;
|
||||||
|
|
||||||
|
private IFrameStableClock gameplayClock = null!;
|
||||||
|
|
||||||
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
ruleset = drawableRuleset;
|
||||||
|
drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this));
|
||||||
|
|
||||||
|
var periods = new List<Period>();
|
||||||
|
|
||||||
|
if (drawableRuleset.Objects.Any())
|
||||||
|
{
|
||||||
|
periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1));
|
||||||
|
|
||||||
|
foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks)
|
||||||
|
periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1));
|
||||||
|
|
||||||
|
static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh);
|
||||||
|
}
|
||||||
|
|
||||||
|
nonGameplayPeriods = new PeriodTracker(periods);
|
||||||
|
|
||||||
|
gameplayClock = drawableRuleset.FrameStableClock;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract bool CheckValidNewAction(OsuAction action);
|
||||||
|
|
||||||
|
private bool checkCorrectAction(OsuAction action)
|
||||||
|
{
|
||||||
|
if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime))
|
||||||
|
{
|
||||||
|
LastAcceptedAction = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case OsuAction.LeftButton:
|
||||||
|
case OsuAction.RightButton:
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Any action which is not left or right button should be ignored.
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CheckValidNewAction(action))
|
||||||
|
{
|
||||||
|
LastAcceptedAction = action;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InputInterceptor : Component, IKeyBindingHandler<OsuAction>
|
||||||
|
{
|
||||||
|
private readonly InputBlockingMod mod;
|
||||||
|
|
||||||
|
public InputInterceptor(InputBlockingMod mod)
|
||||||
|
{
|
||||||
|
this.mod = mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||||
|
// if the pressed action is incorrect, block it from reaching gameplay.
|
||||||
|
=> !mod.checkCorrectAction(e.Action);
|
||||||
|
|
||||||
|
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,119 +1,20 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.Bindings;
|
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Game.Beatmaps.Timing;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
using osu.Game.Screens.Play;
|
|
||||||
using osu.Game.Utils;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModAlternate : Mod, IApplicableToDrawableRuleset<OsuHitObject>
|
public class OsuModAlternate : InputBlockingMod
|
||||||
{
|
{
|
||||||
public override string Name => @"Alternate";
|
public override string Name => @"Alternate";
|
||||||
public override string Acronym => @"AL";
|
public override string Acronym => @"AL";
|
||||||
public override string Description => @"Don't use the same key twice in a row!";
|
public override string Description => @"Don't use the same key twice in a row!";
|
||||||
public override double ScoreMultiplier => 1.0;
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModRelax) };
|
|
||||||
public override ModType Type => ModType.Conversion;
|
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.Keyboard;
|
public override IconUsage? Icon => FontAwesome.Solid.Keyboard;
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModSingleTap) }).ToArray();
|
||||||
|
|
||||||
private const double flash_duration = 1000;
|
protected override bool CheckValidNewAction(OsuAction action) => LastAcceptedAction != action;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A tracker for periods where alternate should not be forced (i.e. non-gameplay periods).
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This is different from <see cref="Player.IsBreakTime"/> in that the periods here end strictly at the first object after the break, rather than the break's end time.
|
|
||||||
/// </remarks>
|
|
||||||
private PeriodTracker nonGameplayPeriods;
|
|
||||||
|
|
||||||
private OsuAction? lastActionPressed;
|
|
||||||
private DrawableRuleset<OsuHitObject> ruleset;
|
|
||||||
|
|
||||||
private IFrameStableClock gameplayClock;
|
|
||||||
|
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
|
||||||
{
|
|
||||||
ruleset = drawableRuleset;
|
|
||||||
drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this));
|
|
||||||
|
|
||||||
var periods = new List<Period>();
|
|
||||||
|
|
||||||
if (drawableRuleset.Objects.Any())
|
|
||||||
{
|
|
||||||
periods.Add(new Period(int.MinValue, getValidJudgementTime(ruleset.Objects.First()) - 1));
|
|
||||||
|
|
||||||
foreach (BreakPeriod b in drawableRuleset.Beatmap.Breaks)
|
|
||||||
periods.Add(new Period(b.StartTime, getValidJudgementTime(ruleset.Objects.First(h => h.StartTime >= b.EndTime)) - 1));
|
|
||||||
|
|
||||||
static double getValidJudgementTime(HitObject hitObject) => hitObject.StartTime - hitObject.HitWindows.WindowFor(HitResult.Meh);
|
|
||||||
}
|
|
||||||
|
|
||||||
nonGameplayPeriods = new PeriodTracker(periods);
|
|
||||||
|
|
||||||
gameplayClock = drawableRuleset.FrameStableClock;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool checkCorrectAction(OsuAction action)
|
|
||||||
{
|
|
||||||
if (nonGameplayPeriods.IsInAny(gameplayClock.CurrentTime))
|
|
||||||
{
|
|
||||||
lastActionPressed = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case OsuAction.LeftButton:
|
|
||||||
case OsuAction.RightButton:
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Any action which is not left or right button should be ignored.
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastActionPressed != action)
|
|
||||||
{
|
|
||||||
// User alternated correctly.
|
|
||||||
lastActionPressed = action;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class InputInterceptor : Component, IKeyBindingHandler<OsuAction>
|
|
||||||
{
|
|
||||||
private readonly OsuModAlternate mod;
|
|
||||||
|
|
||||||
public InputInterceptor(OsuModAlternate mod)
|
|
||||||
{
|
|
||||||
this.mod = mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
|
||||||
// if the pressed action is incorrect, block it from reaching gameplay.
|
|
||||||
=> !mod.checkCorrectAction(e.Action);
|
|
||||||
|
|
||||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModAutoplay : ModAutoplay
|
public class OsuModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModRepel), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate) }).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModRepel), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray();
|
||||||
|
|
||||||
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
|
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModCinema : ModCinema<OsuHitObject>
|
public class OsuModCinema : ModCinema<OsuHitObject>
|
||||||
{
|
{
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate) }).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModAutopilot), typeof(OsuModSpunOut), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray();
|
||||||
|
|
||||||
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
|
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToPlayer
|
public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToPlayer
|
||||||
{
|
{
|
||||||
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
|
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised), typeof(OsuModAlternate) }).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How early before a hitobject's start time to trigger a hit.
|
/// How early before a hitobject's start time to trigger a hit.
|
||||||
|
18
osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs
Normal file
18
osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs
Normal 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 System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
public class OsuModSingleTap : InputBlockingMod
|
||||||
|
{
|
||||||
|
public override string Name => @"Single Tap";
|
||||||
|
public override string Acronym => @"ST";
|
||||||
|
public override string Description => @"You must only use one key!";
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAlternate) }).ToArray();
|
||||||
|
|
||||||
|
protected override bool CheckValidNewAction(OsuAction action) => LastAcceptedAction == null || LastAcceptedAction == action;
|
||||||
|
}
|
||||||
|
}
|
@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
new OsuModClassic(),
|
new OsuModClassic(),
|
||||||
new OsuModRandom(),
|
new OsuModRandom(),
|
||||||
new OsuModMirror(),
|
new OsuModMirror(),
|
||||||
new OsuModAlternate(),
|
new MultiMod(new OsuModAlternate(), new OsuModSingleTap())
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
|
@ -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.14.0" />
|
<PackageReference Include="Realm" Version="10.14.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.707.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.713.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.19.0" />
|
<PackageReference Include="Sentry" Version="3.19.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.32.1" />
|
<PackageReference Include="SharpCompress" Version="0.32.1" />
|
||||||
|
@ -61,7 +61,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="2022.707.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.713.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.702.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
||||||
@ -84,7 +84,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.707.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.713.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.32.1" />
|
<PackageReference Include="SharpCompress" Version="0.32.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user