mirror of
https://github.com/ppy/osu.git
synced 2025-03-05 12:32:58 +08:00
Merge pull request #1908 from andy840119/mania_stage_fix
Split the playfield columns and change the key number by mod
This commit is contained in:
commit
4f360eac56
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The definitions for each stage in a <see cref="ManiaPlayfield"/>.
|
/// The definitions for each stage in a <see cref="ManiaPlayfield"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly List<StageDefinition> Stages = new List<StageDefinition>();
|
public List<StageDefinition> Stages = new List<StageDefinition>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total number of columns represented by all stages in this <see cref="ManiaBeatmap"/>.
|
/// Total number of columns represented by all stages in this <see cref="ManiaBeatmap"/>.
|
||||||
@ -24,10 +24,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="ManiaBeatmap"/>.
|
/// Creates a new <see cref="ManiaBeatmap"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="initialStage">The initial stage.</param>
|
/// <param name="defaultStage">The initial stages.</param>
|
||||||
public ManiaBeatmap(StageDefinition initialStage)
|
public ManiaBeatmap(StageDefinition defaultStage)
|
||||||
{
|
{
|
||||||
Stages.Add(initialStage);
|
Stages.Add(defaultStage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,13 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
public enum ManiaAction
|
public enum ManiaAction
|
||||||
{
|
{
|
||||||
[Description("Special")]
|
[Description("Special 1")]
|
||||||
Special,
|
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 = 10,
|
||||||
[Description("Key 2")]
|
[Description("Key 2")]
|
||||||
@ -36,6 +41,24 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
[Description("Key 8")]
|
[Description("Key 8")]
|
||||||
Key8,
|
Key8,
|
||||||
[Description("Key 9")]
|
[Description("Key 9")]
|
||||||
Key9
|
Key9,
|
||||||
|
[Description("Key 10")]
|
||||||
|
Key10,
|
||||||
|
[Description("Key 11")]
|
||||||
|
Key11,
|
||||||
|
[Description("Key 12")]
|
||||||
|
Key12,
|
||||||
|
[Description("Key 13")]
|
||||||
|
Key13,
|
||||||
|
[Description("Key 14")]
|
||||||
|
Key14,
|
||||||
|
[Description("Key 15")]
|
||||||
|
Key15,
|
||||||
|
[Description("Key 16")]
|
||||||
|
Key16,
|
||||||
|
[Description("Key 17")]
|
||||||
|
Key17,
|
||||||
|
[Description("Key 18")]
|
||||||
|
Key18,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Mods;
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -86,7 +88,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ManiaModRandom(),
|
new ManiaModRandom(),
|
||||||
new ManiaModKeyCoop(),
|
new ManiaModDualStages(),
|
||||||
new MultiMod
|
new MultiMod
|
||||||
{
|
{
|
||||||
Mods = new Mod[]
|
Mods = new Mod[]
|
||||||
@ -117,42 +119,189 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<int> AvailableVariants => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
public override IEnumerable<int> AvailableVariants
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
for (int i = 1; i <= 9; i++)
|
||||||
|
yield return (int)PlayfieldType.Single + i;
|
||||||
|
for (int i = 2; i <= 18; i += 2)
|
||||||
|
yield return (int)PlayfieldType.Dual + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
|
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
|
||||||
{
|
{
|
||||||
var leftKeys = new[]
|
switch (getPlayfieldType(variant))
|
||||||
{
|
{
|
||||||
InputKey.A,
|
case PlayfieldType.Single:
|
||||||
InputKey.S,
|
return new VariantMappingGenerator
|
||||||
InputKey.D,
|
{
|
||||||
InputKey.F
|
LeftKeys = new[]
|
||||||
};
|
{
|
||||||
|
InputKey.A,
|
||||||
|
InputKey.S,
|
||||||
|
InputKey.D,
|
||||||
|
InputKey.F
|
||||||
|
},
|
||||||
|
RightKeys = new[]
|
||||||
|
{
|
||||||
|
InputKey.J,
|
||||||
|
InputKey.K,
|
||||||
|
InputKey.L,
|
||||||
|
InputKey.Semicolon
|
||||||
|
},
|
||||||
|
SpecialKey = InputKey.Space,
|
||||||
|
SpecialAction = ManiaAction.Special1,
|
||||||
|
NormalActionStart = ManiaAction.Key1,
|
||||||
|
}.GenerateKeyBindingsFor(variant, out _);
|
||||||
|
case PlayfieldType.Dual:
|
||||||
|
int keys = getDualStageKeyCount(variant);
|
||||||
|
|
||||||
var rightKeys = new[]
|
var stage1Bindings = new VariantMappingGenerator
|
||||||
{
|
{
|
||||||
InputKey.J,
|
LeftKeys = new[]
|
||||||
InputKey.K,
|
{
|
||||||
InputKey.L,
|
InputKey.Number1,
|
||||||
InputKey.Semicolon
|
InputKey.Number2,
|
||||||
};
|
InputKey.Number3,
|
||||||
|
InputKey.Number4,
|
||||||
|
},
|
||||||
|
RightKeys = new[]
|
||||||
|
{
|
||||||
|
InputKey.Z,
|
||||||
|
InputKey.X,
|
||||||
|
InputKey.C,
|
||||||
|
InputKey.V
|
||||||
|
},
|
||||||
|
SpecialKey = InputKey.Tilde,
|
||||||
|
SpecialAction = ManiaAction.Special1,
|
||||||
|
NormalActionStart = ManiaAction.Key1
|
||||||
|
}.GenerateKeyBindingsFor(keys, out var nextNormal);
|
||||||
|
|
||||||
ManiaAction currentKey = ManiaAction.Key1;
|
var stage2Bindings = new VariantMappingGenerator
|
||||||
|
{
|
||||||
|
LeftKeys = new[]
|
||||||
|
{
|
||||||
|
InputKey.Number7,
|
||||||
|
InputKey.Number8,
|
||||||
|
InputKey.Number9,
|
||||||
|
InputKey.Number0
|
||||||
|
},
|
||||||
|
RightKeys = new[]
|
||||||
|
{
|
||||||
|
InputKey.O,
|
||||||
|
InputKey.P,
|
||||||
|
InputKey.BracketLeft,
|
||||||
|
InputKey.BracketRight
|
||||||
|
},
|
||||||
|
SpecialKey = InputKey.BackSlash,
|
||||||
|
SpecialAction = ManiaAction.Special2,
|
||||||
|
NormalActionStart = nextNormal
|
||||||
|
}.GenerateKeyBindingsFor(keys, out _);
|
||||||
|
|
||||||
var bindings = new List<KeyBinding>();
|
return stage1Bindings.Concat(stage2Bindings);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = leftKeys.Length - variant / 2; i < leftKeys.Length; i++)
|
return new KeyBinding[0];
|
||||||
bindings.Add(new KeyBinding(leftKeys[i], currentKey++));
|
|
||||||
|
|
||||||
for (int i = 0; i < variant / 2; i++)
|
|
||||||
bindings.Add(new KeyBinding(rightKeys[i], currentKey++));
|
|
||||||
|
|
||||||
if (variant % 2 == 1)
|
|
||||||
bindings.Add(new KeyBinding(InputKey.Space, ManiaAction.Special));
|
|
||||||
|
|
||||||
return bindings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetVariantName(int variant) => $"{variant}K";
|
public override string GetVariantName(int variant)
|
||||||
|
{
|
||||||
|
switch (getPlayfieldType(variant))
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
return $"{variant}K";
|
||||||
|
case PlayfieldType.Dual:
|
||||||
|
{
|
||||||
|
var keys = getDualStageKeyCount(variant);
|
||||||
|
return $"{keys}K + {keys}K";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the number of keys for each stage in a <see cref="PlayfieldType.Dual"/> variant.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="variant">The variant.</param>
|
||||||
|
private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the <see cref="PlayfieldType"/> that corresponds to a variant value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="variant">The variant value.</param>
|
||||||
|
/// <returns>The <see cref="PlayfieldType"/> that corresponds to <paramref name="variant"/>.</returns>
|
||||||
|
private PlayfieldType getPlayfieldType(int variant)
|
||||||
|
{
|
||||||
|
return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast<int>().OrderByDescending(i => i).First(v => variant >= v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VariantMappingGenerator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// All the <see cref="InputKey"/>s available to the left hand.
|
||||||
|
/// </summary>
|
||||||
|
public InputKey[] LeftKeys;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All the <see cref="InputKey"/>s available to the right hand.
|
||||||
|
/// </summary>
|
||||||
|
public InputKey[] RightKeys;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="InputKey"/> for the special key.
|
||||||
|
/// </summary>
|
||||||
|
public InputKey SpecialKey;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="ManiaAction"/> at which the normal columns should begin.
|
||||||
|
/// </summary>
|
||||||
|
public ManiaAction NormalActionStart;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="ManiaAction"/> for the special column.
|
||||||
|
/// </summary>
|
||||||
|
public ManiaAction SpecialAction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a list of <see cref="KeyBinding"/>s for a specific number of columns.
|
||||||
|
/// </summary>
|
||||||
|
/// <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>
|
||||||
|
public IEnumerable<KeyBinding> GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction)
|
||||||
|
{
|
||||||
|
ManiaAction currentNormalAction = NormalActionStart;
|
||||||
|
|
||||||
|
var bindings = new List<KeyBinding>();
|
||||||
|
|
||||||
|
for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++)
|
||||||
|
bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++));
|
||||||
|
|
||||||
|
for (int i = 0; i < columns / 2; i++)
|
||||||
|
bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++));
|
||||||
|
|
||||||
|
if (columns % 2 == 1)
|
||||||
|
bindings.Add(new KeyBinding(SpecialKey, SpecialAction));
|
||||||
|
|
||||||
|
nextNormalAction = currentNormalAction;
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PlayfieldType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Columns are grouped into a single stage.
|
||||||
|
/// Number of columns in this stage lies at (item - Single).
|
||||||
|
/// </summary>
|
||||||
|
Single = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// Columns are grouped into two stages.
|
||||||
|
/// Overall number of columns lies at (item - Dual), further computation is required for
|
||||||
|
/// number of columns in each individual stage.
|
||||||
|
/// </summary>
|
||||||
|
Dual = 1000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
osu.Game.Rulesets.Mania/Mods/IPlayfieldTypeMod.cs
Normal file
15
osu.Game.Rulesets.Mania/Mods/IPlayfieldTypeMod.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public interface IPlayfieldTypeMod : IApplicableMod
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="PlayfieldType"/> which this <see cref="IPlayfieldTypeMod"/> requires.
|
||||||
|
/// </summary>
|
||||||
|
PlayfieldType PlayfieldType { get; }
|
||||||
|
}
|
||||||
|
}
|
53
osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
Normal file
53
osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter<ManiaHitObject>, IApplicableToRulesetContainer<ManiaHitObject>
|
||||||
|
{
|
||||||
|
public override string Name => "Dual Stages";
|
||||||
|
public override string ShortenedName => "DS";
|
||||||
|
public override string Description => @"Double the stages, double the fun!";
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
public override bool Ranked => false;
|
||||||
|
|
||||||
|
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
|
||||||
|
{
|
||||||
|
var mbc = (ManiaBeatmapConverter)beatmapConverter;
|
||||||
|
|
||||||
|
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
||||||
|
if (mbc.IsForCurrentRuleset)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mbc.TargetColumns *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
|
||||||
|
{
|
||||||
|
var mrc = (ManiaRulesetContainer)rulesetContainer;
|
||||||
|
|
||||||
|
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
||||||
|
if (mrc.IsForCurrentRuleset)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var newDefinitions = new List<StageDefinition>();
|
||||||
|
foreach (var existing in mrc.Beatmap.Stages)
|
||||||
|
{
|
||||||
|
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||||
|
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
mrc.Beatmap.Stages = newDefinitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayfieldType PlayfieldType => PlayfieldType.Dual;
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
|
||||||
{
|
|
||||||
public class ManiaModKeyCoop : Mod
|
|
||||||
{
|
|
||||||
public override string Name => "KeyCoop";
|
|
||||||
public override string ShortenedName => "2P";
|
|
||||||
public override string Description => @"Double the key amount, double the fun!";
|
|
||||||
public override double ScoreMultiplier => 1;
|
|
||||||
public override bool Ranked => true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
RelativeChildSize = new Vector2(1, 10000),
|
RelativeChildSize = new Vector2(1, 10000),
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new DrawableHoldNote(new HoldNote { Duration = 1 }, ManiaAction.Key1)
|
new DrawableHoldNote(new HoldNote { Duration = 1000 } , ManiaAction.Key1)
|
||||||
{
|
{
|
||||||
Y = 5000,
|
Y = 5000,
|
||||||
Height = 1000,
|
Height = 1000,
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Judgements;
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
@ -31,15 +33,43 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
{
|
{
|
||||||
var rng = new Random(1337);
|
var rng = new Random(1337);
|
||||||
|
|
||||||
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
|
AddStep("1 column", () => createPlayfield(1));
|
||||||
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
|
AddStep("4 columns", () => createPlayfield(4));
|
||||||
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
|
AddStep("5 columns", () => createPlayfield(5));
|
||||||
AddStep("Right special style", () => createPlayfield(4, SpecialColumnPosition.Right));
|
AddStep("8 columns", () => createPlayfield(8));
|
||||||
AddStep("5 columns", () => createPlayfield(5, SpecialColumnPosition.Normal));
|
AddStep("4 + 4 columns", () =>
|
||||||
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
|
{
|
||||||
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
|
var stages = new List<StageDefinition>
|
||||||
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
|
{
|
||||||
AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
|
new StageDefinition { Columns = 4 },
|
||||||
|
new StageDefinition { Columns = 4 },
|
||||||
|
};
|
||||||
|
createPlayfield(stages);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("2 + 4 + 2 columns", () =>
|
||||||
|
{
|
||||||
|
var stages = new List<StageDefinition>
|
||||||
|
{
|
||||||
|
new StageDefinition { Columns = 2 },
|
||||||
|
new StageDefinition { Columns = 4 },
|
||||||
|
new StageDefinition { Columns = 2 },
|
||||||
|
};
|
||||||
|
createPlayfield(stages);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("1 + 8 + 1 columns", () =>
|
||||||
|
{
|
||||||
|
var stages = new List<StageDefinition>
|
||||||
|
{
|
||||||
|
new StageDefinition { Columns = 1 },
|
||||||
|
new StageDefinition { Columns = 8 },
|
||||||
|
new StageDefinition { Columns = 1 },
|
||||||
|
};
|
||||||
|
createPlayfield(stages);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("Reversed", () => createPlayfield(4, true));
|
||||||
|
|
||||||
AddStep("Notes with input", () => createPlayfieldWithNotes());
|
AddStep("Notes with input", () => createPlayfieldWithNotes());
|
||||||
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
|
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
|
||||||
@ -48,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
AddStep("Hit explosion", () =>
|
AddStep("Hit explosion", () =>
|
||||||
{
|
{
|
||||||
var playfield = createPlayfield(4, SpecialColumnPosition.Normal);
|
var playfield = createPlayfield(4);
|
||||||
|
|
||||||
int col = rng.Next(0, 4);
|
int col = rng.Next(0, 4);
|
||||||
|
|
||||||
@ -58,6 +88,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
};
|
};
|
||||||
|
|
||||||
playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
||||||
|
playfield.Columns[col].OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,19 +98,29 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
maniaRuleset = rulesets.GetRuleset(3);
|
maniaRuleset = rulesets.GetRuleset(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
|
private ManiaPlayfield createPlayfield(int cols, bool inverted = false)
|
||||||
|
{
|
||||||
|
var stages = new List<StageDefinition>
|
||||||
|
{
|
||||||
|
new StageDefinition { Columns = cols },
|
||||||
|
};
|
||||||
|
|
||||||
|
return createPlayfield(stages, inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ManiaPlayfield createPlayfield(List<StageDefinition> stages, bool inverted = false)
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
|
|
||||||
var inputManager = new ManiaInputManager(maniaRuleset, cols) { RelativeSizeAxes = Axes.Both };
|
var inputManager = new ManiaInputManager(maniaRuleset, stages.Sum(g => g.Columns)) { RelativeSizeAxes = Axes.Both };
|
||||||
Add(inputManager);
|
Add(inputManager);
|
||||||
|
|
||||||
ManiaPlayfield playfield;
|
ManiaPlayfield playfield;
|
||||||
inputManager.Add(playfield = new ManiaPlayfield(cols)
|
|
||||||
|
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
SpecialColumnPosition = specialPos
|
|
||||||
});
|
});
|
||||||
|
|
||||||
playfield.Inverted.Value = inverted;
|
playfield.Inverted.Value = inverted;
|
||||||
@ -97,7 +138,12 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
Add(inputManager);
|
Add(inputManager);
|
||||||
|
|
||||||
ManiaPlayfield playfield;
|
ManiaPlayfield playfield;
|
||||||
inputManager.Add(playfield = new ManiaPlayfield(4)
|
var stages = new List<StageDefinition>
|
||||||
|
{
|
||||||
|
new StageDefinition { Columns = 4 },
|
||||||
|
};
|
||||||
|
|
||||||
|
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -47,6 +47,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
public Column()
|
public Column()
|
||||||
: base(ScrollingDirection.Up)
|
: base(ScrollingDirection.Up)
|
||||||
{
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
Width = column_width;
|
Width = column_width;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
@ -61,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
Name = "Hit target + hit objects",
|
Name = "Hit target + hit objects",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION },
|
Padding = new MarginPadding { Top = ManiaStage.HIT_TARGET_POSITION },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
@ -115,7 +116,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
Name = "Key",
|
Name = "Key",
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = ManiaPlayfield.HIT_TARGET_POSITION,
|
Height = ManiaStage.HIT_TARGET_POSITION,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
@ -205,12 +206,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
hitObject.Depth = (float)hitObject.HitObject.StartTime;
|
hitObject.Depth = (float)hitObject.HitObject.StartTime;
|
||||||
hitObject.AccentColour = AccentColour;
|
hitObject.AccentColour = AccentColour;
|
||||||
hitObject.OnJudgement += onJudgement;
|
hitObject.OnJudgement += OnJudgement;
|
||||||
|
|
||||||
HitObjects.Add(hitObject);
|
HitObjects.Add(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||||
{
|
{
|
||||||
if (!judgement.IsHit)
|
if (!judgement.IsHit)
|
||||||
return;
|
return;
|
||||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
internal class DrawableManiaJudgement : DrawableJudgement
|
internal class DrawableManiaJudgement : DrawableJudgement
|
||||||
{
|
{
|
||||||
public DrawableManiaJudgement(Judgement judgement)
|
public DrawableManiaJudgement(Judgement judgement)
|
||||||
: base(judgement)
|
: base(judgement)
|
||||||
{
|
{
|
||||||
JudgementText.TextSize = 25;
|
JudgementText.TextSize = 25;
|
||||||
}
|
}
|
||||||
|
@ -3,237 +3,84 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
public class ManiaPlayfield : ScrollingPlayfield
|
public class ManiaPlayfield : ScrollingPlayfield
|
||||||
{
|
{
|
||||||
public const float HIT_TARGET_POSITION = 50;
|
|
||||||
|
|
||||||
private SpecialColumnPosition specialColumnPosition;
|
|
||||||
/// <summary>
|
|
||||||
/// The style to use for the special column.
|
|
||||||
/// </summary>
|
|
||||||
public SpecialColumnPosition SpecialColumnPosition
|
|
||||||
{
|
|
||||||
get { return specialColumnPosition; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (IsLoaded)
|
|
||||||
throw new InvalidOperationException($"Setting {nameof(SpecialColumnPosition)} after the playfield is loaded requires re-creating the playfield.");
|
|
||||||
specialColumnPosition = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this playfield should be inverted. This flips everything inside the playfield.
|
/// Whether this playfield should be inverted. This flips everything inside the playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
|
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
|
||||||
|
|
||||||
private readonly FlowContainer<Column> columns;
|
public List<Column> Columns => stages.SelectMany(x => x.Columns).ToList();
|
||||||
public IEnumerable<Column> Columns => columns.Children;
|
private readonly List<ManiaStage> stages = new List<ManiaStage>();
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
|
||||||
private readonly Container<Drawable> content;
|
|
||||||
|
|
||||||
private List<Color4> normalColumnColours = new List<Color4>();
|
|
||||||
private Color4 specialColumnColour;
|
|
||||||
|
|
||||||
private readonly Container<DrawableManiaJudgement> judgements;
|
|
||||||
|
|
||||||
private readonly int columnCount;
|
|
||||||
|
|
||||||
public ManiaPlayfield(int columnCount)
|
|
||||||
: base(ScrollingDirection.Up)
|
: base(ScrollingDirection.Up)
|
||||||
{
|
{
|
||||||
this.columnCount = columnCount;
|
if (stageDefinitions == null)
|
||||||
|
throw new ArgumentNullException(nameof(stageDefinitions));
|
||||||
|
|
||||||
if (columnCount <= 0)
|
if (stageDefinitions.Count <= 0)
|
||||||
throw new ArgumentException("Can't have zero or fewer columns.");
|
throw new ArgumentException("Can't have zero or fewer stages.");
|
||||||
|
|
||||||
Inverted.Value = true;
|
Inverted.Value = true;
|
||||||
|
|
||||||
Container topLevelContainer;
|
GridContainer playfieldGrid;
|
||||||
InternalChildren = new Drawable[]
|
InternalChild = playfieldGrid = new GridContainer
|
||||||
{
|
{
|
||||||
new Container
|
RelativeSizeAxes = Axes.Both,
|
||||||
{
|
Content = new[] { new Drawable[stageDefinitions.Count] }
|
||||||
Name = "Playfield elements",
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
AutoSizeAxes = Axes.X,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Name = "Columns mask",
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
AutoSizeAxes = Axes.X,
|
|
||||||
Masking = true,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Name = "Background",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4.Black
|
|
||||||
},
|
|
||||||
columns = new FillFlowContainer<Column>
|
|
||||||
{
|
|
||||||
Name = "Columns",
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
AutoSizeAxes = Axes.X,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Padding = new MarginPadding { Left = 1, Right = 1 },
|
|
||||||
Spacing = new Vector2(1, 0)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Name = "Barlines mask",
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Width = 1366, // Bar lines should only be masked on the vertical axis
|
|
||||||
BypassAutoSizeAxes = Axes.Both,
|
|
||||||
Masking = true,
|
|
||||||
Child = content = new Container
|
|
||||||
{
|
|
||||||
Name = "Bar lines",
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
judgements = new Container<DrawableManiaJudgement>
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Y = HIT_TARGET_POSITION + 150,
|
|
||||||
BypassAutoSizeAxes = Axes.Both
|
|
||||||
},
|
|
||||||
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var currentAction = ManiaAction.Key1;
|
var normalColumnAction = ManiaAction.Key1;
|
||||||
for (int i = 0; i < columnCount; i++)
|
var specialColumnAction = ManiaAction.Special1;
|
||||||
|
int firstColumnIndex = 0;
|
||||||
|
for (int i = 0; i < stageDefinitions.Count; i++)
|
||||||
{
|
{
|
||||||
var c = new Column();
|
var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
|
||||||
c.VisibleTimeRange.BindTo(VisibleTimeRange);
|
newStage.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||||
|
newStage.Inverted.BindTo(Inverted);
|
||||||
|
|
||||||
c.IsSpecial = isSpecialColumn(i);
|
playfieldGrid.Content[0][i] = newStage;
|
||||||
c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++;
|
|
||||||
|
|
||||||
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
|
stages.Add(newStage);
|
||||||
|
AddNested(newStage);
|
||||||
|
|
||||||
columns.Add(c);
|
firstColumnIndex += newStage.Columns.Count;
|
||||||
AddNested(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Inverted.ValueChanged += invertedChanged;
|
|
||||||
Inverted.TriggerChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invertedChanged(bool newValue)
|
public override void Add(DrawableHitObject h) => getStageByColumn(((ManiaHitObject)h.HitObject).Column).Add(h);
|
||||||
|
|
||||||
|
public void Add(BarLine barline) => stages.ForEach(s => s.Add(barline));
|
||||||
|
|
||||||
|
private ManiaStage getStageByColumn(int column)
|
||||||
{
|
{
|
||||||
Scale = new Vector2(1, newValue ? -1 : 1);
|
int sum = 0;
|
||||||
judgements.Scale = Scale;
|
foreach (var stage in stages)
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
normalColumnColours = new List<Color4>
|
|
||||||
{
|
{
|
||||||
colours.RedDark,
|
sum = sum + stage.Columns.Count;
|
||||||
colours.GreenDark
|
if (sum > column)
|
||||||
};
|
return stage;
|
||||||
|
|
||||||
specialColumnColour = colours.BlueDark;
|
|
||||||
|
|
||||||
// Set the special column + colour + key
|
|
||||||
foreach (var column in Columns)
|
|
||||||
{
|
|
||||||
if (!column.IsSpecial)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
column.AccentColour = specialColumnColour;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList();
|
return null;
|
||||||
|
|
||||||
// We'll set the colours of the non-special columns in a separate loop, because the non-special
|
|
||||||
// column colours are mirrored across their centre and special styles mess with this
|
|
||||||
for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
|
|
||||||
{
|
|
||||||
Color4 colour = normalColumnColours[i % normalColumnColours.Count];
|
|
||||||
nonSpecialColumns[i].AccentColour = colour;
|
|
||||||
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||||
{
|
{
|
||||||
judgements.Clear();
|
getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement);
|
||||||
judgements.Add(new DrawableManiaJudgement(judgement)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the column index is a special column for this playfield.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="column">The 0-based column index.</param>
|
|
||||||
/// <returns>Whether the column is a special column.</returns>
|
|
||||||
private bool isSpecialColumn(int column)
|
|
||||||
{
|
|
||||||
switch (SpecialColumnPosition)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
case SpecialColumnPosition.Normal:
|
|
||||||
return columnCount % 2 == 1 && column == columnCount / 2;
|
|
||||||
case SpecialColumnPosition.Left:
|
|
||||||
return column == 0;
|
|
||||||
case SpecialColumnPosition.Right:
|
|
||||||
return column == columnCount - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Add(DrawableHitObject h)
|
|
||||||
{
|
|
||||||
h.OnJudgement += OnJudgement;
|
|
||||||
Columns.ElementAt(((ManiaHitObject)h.HitObject).Column).Add(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(DrawableBarLine barline) => HitObjects.Add(barline);
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
// Due to masking differences, it is not possible to get the width of the columns container automatically
|
|
||||||
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
|
|
||||||
content.Width = columns.Width;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenTK;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -12,6 +11,7 @@ using osu.Framework.MathUtils;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
@ -22,6 +22,7 @@ using osu.Game.Rulesets.Replays;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
@ -29,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
|
public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
|
||||||
|
|
||||||
public IEnumerable<DrawableBarLine> BarLines;
|
public IEnumerable<BarLine> BarLines;
|
||||||
|
|
||||||
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||||
@ -38,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||||
|
|
||||||
var timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
var timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
||||||
var barLines = new List<DrawableBarLine>();
|
var barLines = new List<BarLine>();
|
||||||
|
|
||||||
for (int i = 0; i < timingPoints.Count; i++)
|
for (int i = 0; i < timingPoints.Count; i++)
|
||||||
{
|
{
|
||||||
@ -50,12 +51,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
|
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
|
||||||
{
|
{
|
||||||
barLines.Add(new DrawableBarLine(new BarLine
|
barLines.Add(new BarLine
|
||||||
{
|
{
|
||||||
StartTime = t,
|
StartTime = t,
|
||||||
ControlPoint = point,
|
ControlPoint = point,
|
||||||
BeatIndex = index
|
BeatIndex = index
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
BarLines.ForEach(Playfield.Add);
|
BarLines.ForEach(Playfield.Add);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.TotalColumns)
|
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -76,7 +77,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||||
|
|
||||||
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Beatmap.TotalColumns);
|
public override PassThroughInputManager CreateInputManager()
|
||||||
|
{
|
||||||
|
var variantType = Mods.OfType<IPlayfieldTypeMod>().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single;
|
||||||
|
return new ManiaInputManager(Ruleset.RulesetInfo, (int)variantType + Beatmap.TotalColumns);
|
||||||
|
}
|
||||||
|
|
||||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap);
|
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap);
|
||||||
|
|
||||||
|
229
osu.Game.Rulesets.Mania/UI/ManiaStage.cs
Normal file
229
osu.Game.Rulesets.Mania/UI/ManiaStage.cs
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of <see cref="Column"/>s.
|
||||||
|
/// </summary>
|
||||||
|
internal class ManiaStage : ScrollingPlayfield
|
||||||
|
{
|
||||||
|
public const float HIT_TARGET_POSITION = 50;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this playfield should be inverted. This flips everything inside the playfield.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
|
||||||
|
|
||||||
|
public IReadOnlyList<Column> Columns => columnFlow.Children;
|
||||||
|
private readonly FillFlowContainer<Column> columnFlow;
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
private readonly Container<Drawable> content;
|
||||||
|
|
||||||
|
public Container<DrawableManiaJudgement> Judgements => judgements;
|
||||||
|
private readonly Container<DrawableManiaJudgement> judgements;
|
||||||
|
|
||||||
|
private readonly Container topLevelContainer;
|
||||||
|
|
||||||
|
private List<Color4> normalColumnColours = new List<Color4>();
|
||||||
|
private Color4 specialColumnColour;
|
||||||
|
|
||||||
|
private readonly int firstColumnIndex;
|
||||||
|
private readonly StageDefinition definition;
|
||||||
|
|
||||||
|
public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
|
||||||
|
: base(ScrollingDirection.Up)
|
||||||
|
{
|
||||||
|
this.firstColumnIndex = firstColumnIndex;
|
||||||
|
this.definition = definition;
|
||||||
|
|
||||||
|
Name = "Stage";
|
||||||
|
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
AutoSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Name = "Columns mask",
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Name = "Background",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Black
|
||||||
|
},
|
||||||
|
columnFlow = new FillFlowContainer<Column>
|
||||||
|
{
|
||||||
|
Name = "Columns",
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Padding = new MarginPadding { Left = 1, Right = 1 },
|
||||||
|
Spacing = new Vector2(1, 0)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Name = "Barlines mask",
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = 1366, // Bar lines should only be masked on the vertical axis
|
||||||
|
BypassAutoSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Child = content = new Container
|
||||||
|
{
|
||||||
|
Name = "Bar lines",
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
judgements = new Container<DrawableManiaJudgement>
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Y = HIT_TARGET_POSITION + 150,
|
||||||
|
BypassAutoSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < definition.Columns; i++)
|
||||||
|
{
|
||||||
|
var isSpecial = isSpecialColumn(i);
|
||||||
|
var column = new Column
|
||||||
|
{
|
||||||
|
IsSpecial = isSpecial,
|
||||||
|
Action = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++
|
||||||
|
};
|
||||||
|
|
||||||
|
AddColumn(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
Inverted.ValueChanged += invertedChanged;
|
||||||
|
Inverted.TriggerChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invertedChanged(bool newValue)
|
||||||
|
{
|
||||||
|
Scale = new Vector2(1, newValue ? -1 : 1);
|
||||||
|
Judgements.Scale = Scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddColumn(Column c)
|
||||||
|
{
|
||||||
|
c.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||||
|
|
||||||
|
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
|
||||||
|
columnFlow.Add(c);
|
||||||
|
AddNested(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the column index is a special column for this playfield.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="column">The 0-based column index.</param>
|
||||||
|
/// <returns>Whether the column is a special column.</returns>
|
||||||
|
private bool isSpecialColumn(int column) => definition.Columns % 2 == 1 && column == definition.Columns / 2;
|
||||||
|
|
||||||
|
public override void Add(DrawableHitObject h)
|
||||||
|
{
|
||||||
|
var maniaObject = (ManiaHitObject)h.HitObject;
|
||||||
|
int columnIndex = maniaObject.Column - firstColumnIndex;
|
||||||
|
Columns.ElementAt(columnIndex).Add(h);
|
||||||
|
h.OnJudgement += OnJudgement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
|
||||||
|
|
||||||
|
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||||
|
{
|
||||||
|
judgements.Clear();
|
||||||
|
judgements.Add(new DrawableManiaJudgement(judgement)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
normalColumnColours = new List<Color4>
|
||||||
|
{
|
||||||
|
colours.RedDark,
|
||||||
|
colours.GreenDark
|
||||||
|
};
|
||||||
|
|
||||||
|
specialColumnColour = colours.BlueDark;
|
||||||
|
|
||||||
|
// Set the special column + colour + key
|
||||||
|
foreach (var column in Columns)
|
||||||
|
{
|
||||||
|
if (!column.IsSpecial)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
column.AccentColour = specialColumnColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nonSpecialColumns = Columns.Where(c => !c.IsSpecial).ToList();
|
||||||
|
|
||||||
|
// We'll set the colours of the non-special columns in a separate loop, because the non-special
|
||||||
|
// column colours are mirrored across their centre and special styles mess with this
|
||||||
|
for (int i = 0; i < Math.Ceiling(nonSpecialColumns.Count / 2f); i++)
|
||||||
|
{
|
||||||
|
Color4 colour = normalColumnColours[i % normalColumnColours.Count];
|
||||||
|
nonSpecialColumns[i].AccentColour = colour;
|
||||||
|
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
// Due to masking differences, it is not possible to get the width of the columns container automatically
|
||||||
|
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
|
||||||
|
content.Width = columnFlow.Width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
|
||||||
{
|
|
||||||
public enum SpecialColumnPosition
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The special column will lie in the center of the columns.
|
|
||||||
/// </summary>
|
|
||||||
Normal,
|
|
||||||
/// <summary>
|
|
||||||
/// The special column will lie to the left of the columns.
|
|
||||||
/// </summary>
|
|
||||||
Left,
|
|
||||||
/// <summary>
|
|
||||||
/// The special column will lie to the right of the columns.
|
|
||||||
/// </summary>
|
|
||||||
Right
|
|
||||||
}
|
|
||||||
}
|
|
@ -64,6 +64,7 @@
|
|||||||
<Compile Include="Judgements\HoldNoteTickJudgement.cs" />
|
<Compile Include="Judgements\HoldNoteTickJudgement.cs" />
|
||||||
<Compile Include="Judgements\ManiaJudgement.cs" />
|
<Compile Include="Judgements\ManiaJudgement.cs" />
|
||||||
<Compile Include="ManiaDifficultyCalculator.cs" />
|
<Compile Include="ManiaDifficultyCalculator.cs" />
|
||||||
|
<Compile Include="Mods\IPlayfieldTypeMod.cs" />
|
||||||
<Compile Include="Mods\ManiaKeyMod.cs" />
|
<Compile Include="Mods\ManiaKeyMod.cs" />
|
||||||
<Compile Include="Mods\ManiaModAutoplay.cs" />
|
<Compile Include="Mods\ManiaModAutoplay.cs" />
|
||||||
<Compile Include="Mods\ManiaModDaycore.cs" />
|
<Compile Include="Mods\ManiaModDaycore.cs" />
|
||||||
@ -83,7 +84,7 @@
|
|||||||
<Compile Include="Mods\ManiaModKey7.cs" />
|
<Compile Include="Mods\ManiaModKey7.cs" />
|
||||||
<Compile Include="Mods\ManiaModKey8.cs" />
|
<Compile Include="Mods\ManiaModKey8.cs" />
|
||||||
<Compile Include="Mods\ManiaModKey9.cs" />
|
<Compile Include="Mods\ManiaModKey9.cs" />
|
||||||
<Compile Include="Mods\ManiaModKeyCoop.cs" />
|
<Compile Include="Mods\ManiaModDualStages.cs" />
|
||||||
<Compile Include="Mods\ManiaModNightcore.cs" />
|
<Compile Include="Mods\ManiaModNightcore.cs" />
|
||||||
<Compile Include="Mods\ManiaModPerfect.cs" />
|
<Compile Include="Mods\ManiaModPerfect.cs" />
|
||||||
<Compile Include="Mods\ManiaModRandom.cs" />
|
<Compile Include="Mods\ManiaModRandom.cs" />
|
||||||
@ -115,12 +116,12 @@
|
|||||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||||
<Compile Include="UI\Column.cs" />
|
<Compile Include="UI\Column.cs" />
|
||||||
<Compile Include="UI\DrawableManiaJudgement.cs" />
|
<Compile Include="UI\DrawableManiaJudgement.cs" />
|
||||||
|
<Compile Include="UI\ManiaStage.cs" />
|
||||||
<Compile Include="UI\HitExplosion.cs" />
|
<Compile Include="UI\HitExplosion.cs" />
|
||||||
<Compile Include="UI\ManiaRulesetContainer.cs" />
|
<Compile Include="UI\ManiaRulesetContainer.cs" />
|
||||||
<Compile Include="UI\ManiaPlayfield.cs" />
|
<Compile Include="UI\ManiaPlayfield.cs" />
|
||||||
<Compile Include="ManiaRuleset.cs" />
|
<Compile Include="ManiaRuleset.cs" />
|
||||||
<Compile Include="Mods\ManiaModNoFail.cs" />
|
<Compile Include="Mods\ManiaModNoFail.cs" />
|
||||||
<Compile Include="UI\SpecialColumnPosition.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<None Include="app.config" />
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Input.Bindings
|
|||||||
|
|
||||||
private KeyBindingStore store;
|
private KeyBindingStore store;
|
||||||
|
|
||||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings();
|
public override IEnumerable<KeyBinding> DefaultKeyBindings => ruleset.CreateInstance().GetDefaultKeyBindings(variant ?? 0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new instance.
|
/// Create a new instance.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -36,8 +35,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width.</param>
|
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width.</param>
|
||||||
protected Playfield(float? customWidth = null)
|
protected Playfield(float? customWidth = null)
|
||||||
{
|
{
|
||||||
// Default height since we force relative size axes
|
RelativeSizeAxes = Axes.Both;
|
||||||
Size = Vector2.One;
|
|
||||||
|
|
||||||
AddInternal(ScaledContent = new ScaledContainer
|
AddInternal(ScaledContent = new ScaledContainer
|
||||||
{
|
{
|
||||||
@ -62,12 +60,6 @@ namespace osu.Game.Rulesets.UI
|
|||||||
Add(HitObjects);
|
Add(HitObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Axes RelativeSizeAxes
|
|
||||||
{
|
|
||||||
get { return Axes.Both; }
|
|
||||||
set { throw new InvalidOperationException($@"{nameof(Playfield)}'s {nameof(RelativeSizeAxes)} should never be changed from {Axes.Both}"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield.
|
/// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the specified beatmap is assumed to be specific to the current ruleset.
|
/// Whether the specified beatmap is assumed to be specific to the current ruleset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly bool IsForCurrentRuleset;
|
public readonly bool IsForCurrentRuleset;
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);
|
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user