1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 23:12:56 +08:00

Merge pull request #29342 from cl8n/remove-mania-action-special

Remove "Special" `ManiaAction`s for center columns
This commit is contained in:
Dan Balasescu 2024-08-13 15:36:36 +09:00 committed by GitHub
commit 4b4f0ecf7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 85 additions and 175 deletions

View File

@ -12,8 +12,8 @@ namespace osu.Game.Rulesets.Mania.Tests
{ {
[TestCase(ManiaAction.Key1)] [TestCase(ManiaAction.Key1)]
[TestCase(ManiaAction.Key1, ManiaAction.Key2)] [TestCase(ManiaAction.Key1, ManiaAction.Key2)]
[TestCase(ManiaAction.Special1)] [TestCase(ManiaAction.Key5)]
[TestCase(ManiaAction.Key8)] [TestCase(ManiaAction.Key9)]
public void TestEncodeDecodeSingleStage(params ManiaAction[] actions) public void TestEncodeDecodeSingleStage(params ManiaAction[] actions)
{ {
var beatmap = new ManiaBeatmap(new StageDefinition(9)); var beatmap = new ManiaBeatmap(new StageDefinition(9));
@ -29,11 +29,11 @@ namespace osu.Game.Rulesets.Mania.Tests
[TestCase(ManiaAction.Key1)] [TestCase(ManiaAction.Key1)]
[TestCase(ManiaAction.Key1, ManiaAction.Key2)] [TestCase(ManiaAction.Key1, ManiaAction.Key2)]
[TestCase(ManiaAction.Special1)] [TestCase(ManiaAction.Key3)]
[TestCase(ManiaAction.Special2)]
[TestCase(ManiaAction.Special1, ManiaAction.Special2)]
[TestCase(ManiaAction.Special1, ManiaAction.Key5)]
[TestCase(ManiaAction.Key8)] [TestCase(ManiaAction.Key8)]
[TestCase(ManiaAction.Key3, ManiaAction.Key8)]
[TestCase(ManiaAction.Key3, ManiaAction.Key6)]
[TestCase(ManiaAction.Key10)]
public void TestEncodeDecodeDualStage(params ManiaAction[] actions) public void TestEncodeDecodeDualStage(params ManiaAction[] actions)
{ {
var beatmap = new ManiaBeatmap(new StageDefinition(5)); var beatmap = new ManiaBeatmap(new StageDefinition(5));

View File

@ -14,12 +14,11 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{ {
SetContents(_ => SetContents(_ =>
{ {
ManiaAction normalAction = ManiaAction.Key1; ManiaAction action = ManiaAction.Key1;
ManiaAction specialAction = ManiaAction.Special1;
return new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4) return new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4)
{ {
Child = new Stage(0, new StageDefinition(4), ref normalAction, ref specialAction) Child = new Stage(0, new StageDefinition(4), ref action)
}; };
}); });
} }

View File

@ -36,8 +36,8 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames"); Assert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames");
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time"); Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time"); Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed"); Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released"); Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
} }
[Test] [Test]
@ -57,8 +57,8 @@ namespace osu.Game.Rulesets.Mania.Tests
Assert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames"); Assert.AreEqual(generated.Frames.Count, frame_offset + 2, "Incorrect number of frames");
Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time"); Assert.AreEqual(1000, generated.Frames[frame_offset].Time, "Incorrect hit time");
Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time"); Assert.AreEqual(3000, generated.Frames[frame_offset + 1].Time, "Incorrect release time");
Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Special1), "Special1 has not been pressed"); Assert.IsTrue(checkContains(generated.Frames[frame_offset], ManiaAction.Key1), "Key1 has not been pressed");
Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Special1), "Special1 has not been released"); Assert.IsFalse(checkContains(generated.Frames[frame_offset + 1], ManiaAction.Key1), "Key1 has not been released");
} }
[Test] [Test]

View File

@ -131,9 +131,7 @@ namespace osu.Game.Rulesets.Mania.Tests
private ScrollingTestContainer createStage(ScrollingDirection direction, ManiaAction action) private ScrollingTestContainer createStage(ScrollingDirection direction, ManiaAction action)
{ {
var specialAction = ManiaAction.Special1; var stage = new Stage(0, new StageDefinition(2), ref action);
var stage = new Stage(0, new StageDefinition(2), ref action, ref specialAction);
stages.Add(stage); stages.Add(stage);
return new ScrollingTestContainer(direction) return new ScrollingTestContainer(direction)

View File

@ -45,18 +45,15 @@ namespace osu.Game.Rulesets.Mania
LeftKeys = stage1LeftKeys, LeftKeys = stage1LeftKeys,
RightKeys = stage1RightKeys, RightKeys = stage1RightKeys,
SpecialKey = InputKey.V, SpecialKey = InputKey.V,
SpecialAction = ManiaAction.Special1, }.GenerateKeyBindingsFor(singleStageVariant);
NormalActionStart = ManiaAction.Key1
}.GenerateKeyBindingsFor(singleStageVariant, out var nextNormal);
var stage2Bindings = new VariantMappingGenerator var stage2Bindings = new VariantMappingGenerator
{ {
LeftKeys = stage2LeftKeys, LeftKeys = stage2LeftKeys,
RightKeys = stage2RightKeys, RightKeys = stage2RightKeys,
SpecialKey = InputKey.B, SpecialKey = InputKey.B,
SpecialAction = ManiaAction.Special2, ActionStart = (ManiaAction)singleStageVariant,
NormalActionStart = nextNormal }.GenerateKeyBindingsFor(singleStageVariant);
}.GenerateKeyBindingsFor(singleStageVariant, out _);
return stage1Bindings.Concat(stage2Bindings); return stage1Bindings.Concat(stage2Bindings);
} }

View File

@ -19,16 +19,8 @@ namespace osu.Game.Rulesets.Mania
public enum ManiaAction public enum ManiaAction
{ {
[Description("Special 1")]
Special1 = 1,
[Description("Special 2")]
Special2,
// This offsets the start value of normal keys in-case we add more special keys
// above at a later time, without breaking replays/configs.
[Description("Key 1")] [Description("Key 1")]
Key1 = 10, Key1,
[Description("Key 2")] [Description("Key 2")]
Key2, Key2,

View File

@ -17,28 +17,9 @@ namespace osu.Game.Rulesets.Mania.Replays
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap; public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
private readonly ManiaAction[] columnActions;
public ManiaAutoGenerator(ManiaBeatmap beatmap) public ManiaAutoGenerator(ManiaBeatmap beatmap)
: base(beatmap) : base(beatmap)
{ {
columnActions = new ManiaAction[Beatmap.TotalColumns];
var normalAction = ManiaAction.Key1;
var specialAction = ManiaAction.Special1;
int totalCounter = 0;
foreach (var stage in Beatmap.Stages)
{
for (int i = 0; i < stage.Columns; i++)
{
if (stage.IsSpecialColumn(i))
columnActions[totalCounter] = specialAction++;
else
columnActions[totalCounter] = normalAction++;
totalCounter++;
}
}
} }
protected override void GenerateFrames() protected override void GenerateFrames()
@ -57,11 +38,11 @@ namespace osu.Game.Rulesets.Mania.Replays
switch (point) switch (point)
{ {
case HitPoint: case HitPoint:
actions.Add(columnActions[point.Column]); actions.Add(ManiaAction.Key1 + point.Column);
break; break;
case ReleasePoint: case ReleasePoint:
actions.Remove(columnActions[point.Column]); actions.Remove(ManiaAction.Key1 + point.Column);
break; break;
} }
} }

View File

@ -1,11 +1,9 @@
// 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 osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Replays.Legacy; using osu.Game.Replays.Legacy;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
@ -27,118 +25,27 @@ namespace osu.Game.Rulesets.Mania.Replays
public void FromLegacy(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame? lastFrame = null) public void FromLegacy(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame? lastFrame = null)
{ {
var maniaBeatmap = (ManiaBeatmap)beatmap; var action = ManiaAction.Key1;
var normalAction = ManiaAction.Key1;
var specialAction = ManiaAction.Special1;
int activeColumns = (int)(legacyFrame.MouseX ?? 0); int activeColumns = (int)(legacyFrame.MouseX ?? 0);
int counter = 0;
while (activeColumns > 0) while (activeColumns > 0)
{ {
bool isSpecial = isColumnAtIndexSpecial(maniaBeatmap, counter);
if ((activeColumns & 1) > 0) if ((activeColumns & 1) > 0)
Actions.Add(isSpecial ? specialAction : normalAction); Actions.Add(action);
if (isSpecial) action++;
specialAction++;
else
normalAction++;
counter++;
activeColumns >>= 1; activeColumns >>= 1;
} }
} }
public LegacyReplayFrame ToLegacy(IBeatmap beatmap) public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
{ {
var maniaBeatmap = (ManiaBeatmap)beatmap;
int keys = 0; int keys = 0;
foreach (var action in Actions) foreach (var action in Actions)
{ keys |= 1 << (int)action;
switch (action)
{
case ManiaAction.Special1:
keys |= 1 << getSpecialColumnIndex(maniaBeatmap, 0);
break;
case ManiaAction.Special2:
keys |= 1 << getSpecialColumnIndex(maniaBeatmap, 1);
break;
default:
// the index in lazer, which doesn't include special keys.
int nonSpecialKeyIndex = action - ManiaAction.Key1;
// the index inclusive of special keys.
int overallIndex = 0;
// iterate to find the index including special keys.
for (; overallIndex < maniaBeatmap.TotalColumns; overallIndex++)
{
// skip over special columns.
if (isColumnAtIndexSpecial(maniaBeatmap, overallIndex))
continue;
// found a non-special column to use.
if (nonSpecialKeyIndex == 0)
break;
// found a non-special column but not ours.
nonSpecialKeyIndex--;
}
keys |= 1 << overallIndex;
break;
}
}
return new LegacyReplayFrame(Time, keys, null, ReplayButtonState.None); return new LegacyReplayFrame(Time, keys, null, ReplayButtonState.None);
} }
/// <summary>
/// Find the overall index (across all stages) for a specified special key.
/// </summary>
/// <param name="maniaBeatmap">The beatmap.</param>
/// <param name="specialOffset">The special key offset (0 is S1).</param>
/// <returns>The overall index for the special column.</returns>
private int getSpecialColumnIndex(ManiaBeatmap maniaBeatmap, int specialOffset)
{
for (int i = 0; i < maniaBeatmap.TotalColumns; i++)
{
if (isColumnAtIndexSpecial(maniaBeatmap, i))
{
if (specialOffset == 0)
return i;
specialOffset--;
}
}
throw new ArgumentException("Special key index is too high.", nameof(specialOffset));
}
/// <summary>
/// Check whether the column at an overall index (across all stages) is a special column.
/// </summary>
/// <param name="beatmap">The beatmap.</param>
/// <param name="index">The overall index to check.</param>
private bool isColumnAtIndexSpecial(ManiaBeatmap beatmap, int index)
{
foreach (var stage in beatmap.Stages)
{
if (index >= stage.Columns)
{
index -= stage.Columns;
continue;
}
return stage.IsSpecialColumn(index);
}
throw new ArgumentException("Column index is too high.", nameof(index));
}
} }
} }

View File

@ -34,8 +34,6 @@ namespace osu.Game.Rulesets.Mania
LeftKeys = leftKeys, LeftKeys = leftKeys,
RightKeys = rightKeys, RightKeys = rightKeys,
SpecialKey = InputKey.Space, SpecialKey = InputKey.Space,
SpecialAction = ManiaAction.Special1, }.GenerateKeyBindingsFor(variant);
NormalActionStart = ManiaAction.Key1,
}.GenerateKeyBindingsFor(variant, out _);
} }
} }

View File

@ -66,13 +66,12 @@ namespace osu.Game.Rulesets.Mania.UI
Content = new[] { new Drawable[stageDefinitions.Count] } Content = new[] { new Drawable[stageDefinitions.Count] }
}); });
var normalColumnAction = ManiaAction.Key1; var columnAction = ManiaAction.Key1;
var specialColumnAction = ManiaAction.Special1;
int firstColumnIndex = 0; int firstColumnIndex = 0;
for (int i = 0; i < stageDefinitions.Count; i++) for (int i = 0; i < stageDefinitions.Count; i++)
{ {
var newStage = new Stage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction); var newStage = new Stage(firstColumnIndex, stageDefinitions[i], ref columnAction);
playfieldGrid.Content[0][i] = newStage; playfieldGrid.Content[0][i] = newStage;

View File

@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Mania.UI
private ISkinSource currentSkin = null!; private ISkinSource currentSkin = null!;
public Stage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction) public Stage(int firstColumnIndex, StageDefinition definition, ref ManiaAction columnStartAction)
{ {
this.firstColumnIndex = firstColumnIndex; this.firstColumnIndex = firstColumnIndex;
Definition = definition; Definition = definition;
@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Width = 1, Width = 1,
Action = { Value = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ } Action = { Value = columnStartAction++ }
}; };
topLevelContainer.Add(column.TopLevelContainer.CreateProxy()); topLevelContainer.Add(column.TopLevelContainer.CreateProxy());

View File

@ -26,37 +26,30 @@ namespace osu.Game.Rulesets.Mania
public InputKey SpecialKey; public InputKey SpecialKey;
/// <summary> /// <summary>
/// The <see cref="ManiaAction"/> at which the normal columns should begin. /// The <see cref="ManiaAction"/> at which the columns should begin.
/// </summary> /// </summary>
public ManiaAction NormalActionStart; public ManiaAction ActionStart;
/// <summary>
/// The <see cref="ManiaAction"/> for the special column.
/// </summary>
public ManiaAction SpecialAction;
/// <summary> /// <summary>
/// Generates a list of <see cref="KeyBinding"/>s for a specific number of columns. /// Generates a list of <see cref="KeyBinding"/>s for a specific number of columns.
/// </summary> /// </summary>
/// <param name="columns">The number of columns that need to be bound.</param> /// <param name="columns">The number of columns that need to be bound.</param>
/// <param name="nextNormalAction">The next <see cref="ManiaAction"/> to use for normal columns.</param>
/// <returns>The keybindings.</returns> /// <returns>The keybindings.</returns>
public IEnumerable<KeyBinding> GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction) public IEnumerable<KeyBinding> GenerateKeyBindingsFor(int columns)
{ {
ManiaAction currentNormalAction = NormalActionStart; ManiaAction currentAction = ActionStart;
var bindings = new List<KeyBinding>(); var bindings = new List<KeyBinding>();
for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++) for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++)
bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++)); bindings.Add(new KeyBinding(LeftKeys[i], currentAction++));
if (columns % 2 == 1) if (columns % 2 == 1)
bindings.Add(new KeyBinding(SpecialKey, SpecialAction)); bindings.Add(new KeyBinding(SpecialKey, currentAction++));
for (int i = 0; i < columns / 2; i++) for (int i = 0; i < columns / 2; i++)
bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++)); bindings.Add(new KeyBinding(RightKeys[i], currentAction++));
nextNormalAction = currentNormalAction;
return bindings; return bindings;
} }
} }

View File

@ -107,7 +107,7 @@ namespace osu.Game.Tests.Visual.Gameplay
KeyCounter counter = null!; KeyCounter counter = null!;
loadPlayer(() => new ManiaRuleset()); loadPlayer(() => new ManiaRuleset());
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Special1)); AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Key4));
checkKey(() => counter, 0, false); checkKey(() => counter, 0, false);
AddStep("press space", () => InputManager.PressKey(Key.Space)); AddStep("press space", () => InputManager.PressKey(Key.Space));
@ -174,7 +174,7 @@ namespace osu.Game.Tests.Visual.Gameplay
KeyCounter counter = null!; KeyCounter counter = null!;
loadPlayer(() => new ManiaRuleset()); loadPlayer(() => new ManiaRuleset());
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Special1)); AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Key4));
AddStep("press space", () => InputManager.PressKey(Key.Space)); AddStep("press space", () => InputManager.PressKey(Key.Space));
AddStep("pause", () => Player.Pause()); AddStep("pause", () => Player.Pause());
@ -237,7 +237,7 @@ namespace osu.Game.Tests.Visual.Gameplay
KeyCounter counter = null!; KeyCounter counter = null!;
loadPlayer(() => new ManiaRuleset()); loadPlayer(() => new ManiaRuleset());
AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Special1)); AddStep("get key counter", () => counter = this.ChildrenOfType<KeyCounter>().Single(k => k.Trigger is KeyCounterActionTrigger<ManiaAction> actionTrigger && actionTrigger.Action == ManiaAction.Key4));
AddStep("press space", () => InputManager.PressKey(Key.Space)); AddStep("press space", () => InputManager.PressKey(Key.Space));
checkKey(() => counter, 1, true); checkKey(() => counter, 1, true);

View File

@ -92,8 +92,9 @@ namespace osu.Game.Database
/// 39 2023-12-19 Migrate any EndTimeObjectCount and TotalObjectCount values of 0 to -1 to better identify non-calculated values. /// 39 2023-12-19 Migrate any EndTimeObjectCount and TotalObjectCount values of 0 to -1 to better identify non-calculated values.
/// 40 2023-12-21 Add ScoreInfo.Version to keep track of which build scores were set on. /// 40 2023-12-21 Add ScoreInfo.Version to keep track of which build scores were set on.
/// 41 2024-04-17 Add ScoreInfo.TotalScoreWithoutMods for future mod multiplier rebalances. /// 41 2024-04-17 Add ScoreInfo.TotalScoreWithoutMods for future mod multiplier rebalances.
/// 42 2024-08-07 Update mania key bindings to reflect changes to ManiaAction
/// </summary> /// </summary>
private const int schema_version = 41; private const int schema_version = 42;
/// <summary> /// <summary>
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods. /// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
@ -1145,6 +1146,51 @@ namespace osu.Game.Database
} }
} }
break;
case 42:
for (int columns = 1; columns <= 10; columns++)
{
remapKeyBindingsForVariant(columns, false);
remapKeyBindingsForVariant(columns, true);
}
// Replace existing key bindings with new ones reflecting changes to ManiaAction:
// - "Special#" actions are removed and "Key#" actions are inserted in their place.
// - All actions are renumbered to remove the old offsets.
void remapKeyBindingsForVariant(int columns, bool dual)
{
// https://github.com/ppy/osu/blob/8773c2f7ebc226942d6124eb95c07a83934272ea/osu.Game.Rulesets.Mania/ManiaRuleset.cs#L327-L336
int variant = dual ? 1000 + (columns * 2) : columns;
var oldKeyBindingsQuery = migration.NewRealm
.All<RealmKeyBinding>()
.Where(kb => kb.RulesetName == @"mania" && kb.Variant == variant);
var oldKeyBindings = oldKeyBindingsQuery.Detach();
migration.NewRealm.RemoveRange(oldKeyBindingsQuery);
// https://github.com/ppy/osu/blob/8773c2f7ebc226942d6124eb95c07a83934272ea/osu.Game.Rulesets.Mania/ManiaInputManager.cs#L22-L31
int oldNormalAction = 10; // Old Key1 offset
int oldSpecialAction = 1; // Old Special1 offset
for (int column = 0; column < columns * (dual ? 2 : 1); column++)
{
if (columns % 2 == 1 && column % columns == columns / 2)
remapKeyBinding(oldSpecialAction++, column);
else
remapKeyBinding(oldNormalAction++, column);
}
void remapKeyBinding(int oldAction, int newAction)
{
var oldKeyBinding = oldKeyBindings.Find(kb => kb.ActionInt == oldAction);
if (oldKeyBinding != null)
migration.NewRealm.Add(new RealmKeyBinding(newAction, oldKeyBinding.KeyCombination, @"mania", variant));
}
}
break; break;
} }