1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-21 03:02:54 +08:00

Merge branch 'master' into scrolling-hitobjects-rewrite

This commit is contained in:
smoogipoo 2018-01-05 14:58:57 +09:00
commit 1276e2c71a
28 changed files with 261 additions and 165 deletions

View File

@ -0,0 +1,33 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Beatmaps
{
public class ManiaBeatmap : Beatmap<ManiaHitObject>
{
/// <summary>
/// The definitions for each stage in a <see cref="ManiaPlayfield"/>.
/// </summary>
public readonly List<StageDefinition> Stages = new List<StageDefinition>();
/// <summary>
/// Total number of columns represented by all stages in this <see cref="ManiaBeatmap"/>.
/// </summary>
public int TotalColumns => Stages.Sum(g => g.Columns);
/// <summary>
/// Creates a new <see cref="ManiaBeatmap"/>.
/// </summary>
/// <param name="initialStage">The initial stage.</param>
public ManiaBeatmap(StageDefinition initialStage)
{
Stages.Add(initialStage);
}
}
}

View File

@ -3,6 +3,7 @@
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using System; using System;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -24,24 +25,39 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
public int TargetColumns;
public readonly bool IsForCurrentRuleset;
private Pattern lastPattern = new Pattern(); private Pattern lastPattern = new Pattern();
private FastRandom random; private FastRandom random;
private Beatmap beatmap;
private readonly int availableColumns; private ManiaBeatmap beatmap;
private readonly bool isForCurrentRuleset;
public ManiaBeatmapConverter(bool isForCurrentRuleset, int availableColumns) public ManiaBeatmapConverter(bool isForCurrentRuleset, Beatmap original)
{ {
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns)); IsForCurrentRuleset = isForCurrentRuleset;
this.isForCurrentRuleset = isForCurrentRuleset; var roundedCircleSize = Math.Round(original.BeatmapInfo.BaseDifficulty.CircleSize);
this.availableColumns = availableColumns; var roundedOverallDifficulty = Math.Round(original.BeatmapInfo.BaseDifficulty.OverallDifficulty);
if (isForCurrentRuleset)
TargetColumns = (int)Math.Max(1, roundedCircleSize);
else
{
float percentSliderOrSpinner = (float)original.HitObjects.Count(h => h is IHasEndTime) / original.HitObjects.Count;
if (percentSliderOrSpinner < 0.2)
TargetColumns = 7;
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
TargetColumns = roundedOverallDifficulty > 5 ? 7 : 6;
else if (percentSliderOrSpinner > 0.6)
TargetColumns = roundedOverallDifficulty > 4 ? 5 : 4;
else
TargetColumns = Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7));
}
} }
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original) protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original)
{ {
beatmap = original;
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty; BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
@ -51,6 +67,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
return base.ConvertBeatmap(original); return base.ConvertBeatmap(original);
} }
protected override Beatmap<ManiaHitObject> CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns });
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap) protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
{ {
var maniaOriginal = original as ManiaHitObject; var maniaOriginal = original as ManiaHitObject;
@ -60,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
yield break; yield break;
} }
var objects = isForCurrentRuleset ? generateSpecific(original) : generateConverted(original); var objects = IsForCurrentRuleset ? generateSpecific(original) : generateConverted(original);
if (objects == null) if (objects == null)
yield break; yield break;
@ -96,7 +114,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// <returns>The hit objects generated.</returns> /// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original) private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
{ {
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, availableColumns, lastPattern); var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern);
Pattern newPattern = generator.Generate(); Pattern newPattern = generator.Generate();
lastPattern = newPattern; lastPattern = newPattern;
@ -120,14 +138,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
Patterns.PatternGenerator conversion = null; Patterns.PatternGenerator conversion = null;
if (distanceData != null) if (distanceData != null)
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern); conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern);
else if (endTimeData != null) else if (endTimeData != null)
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, availableColumns); conversion = new EndTimeObjectPatternGenerator(random, original, beatmap);
else if (positionData != null) else if (positionData != null)
{ {
computeDensity(original.StartTime); computeDensity(original.StartTime);
conversion = new HitObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern, lastTime, lastPosition, density, lastStair); conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair);
recordNote(original.StartTime, positionData.Position); recordNote(original.StartTime, positionData.Position);
} }
@ -149,8 +167,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// </summary> /// </summary>
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{ {
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern) public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
: base(random, hitObject, beatmap, availableColumns, previousPattern) : base(random, hitObject, beatmap, previousPattern)
{ {
} }

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -30,8 +29,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private PatternType convertType; private PatternType convertType;
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern) public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
: base(random, hitObject, beatmap, availableColumns, previousPattern) : base(random, hitObject, beatmap, previousPattern)
{ {
convertType = PatternType.None; convertType = PatternType.None;
if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
@ -79,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (duration >= 4000) if (duration >= 4000)
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0); return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
if (segmentDuration > 400 && repeatCount < AvailableColumns - 1 - RandomStart) if (segmentDuration > 400 && repeatCount < TotalColumns - 1 - RandomStart)
return generateTiledHoldNotes(HitObject.StartTime); return generateTiledHoldNotes(HitObject.StartTime);
return generateHoldAndNormalNotes(HitObject.StartTime); return generateHoldAndNormalNotes(HitObject.StartTime);
@ -87,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (segmentDuration <= 110) if (segmentDuration <= 110)
{ {
if (PreviousPattern.ColumnWithObjects < AvailableColumns) if (PreviousPattern.ColumnWithObjects < TotalColumns)
convertType |= PatternType.ForceNotStack; convertType |= PatternType.ForceNotStack;
else else
convertType &= ~PatternType.ForceNotStack; convertType &= ~PatternType.ForceNotStack;
@ -135,12 +134,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern(); var pattern = new Pattern();
int usableColumns = AvailableColumns - RandomStart - PreviousPattern.ColumnWithObjects; int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects;
int nextColumn = Random.Next(RandomStart, AvailableColumns); int nextColumn = Random.Next(RandomStart, TotalColumns);
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
{ {
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
nextColumn = Random.Next(RandomStart, AvailableColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(pattern, nextColumn, startTime, endTime); addToPattern(pattern, nextColumn, startTime, endTime);
} }
@ -148,7 +147,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
for (int i = 0; i < noteCount - usableColumns; i++) for (int i = 0; i < noteCount - usableColumns; i++)
{ {
while (pattern.ColumnHasObject(nextColumn)) while (pattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, AvailableColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(pattern, nextColumn, startTime, endTime); addToPattern(pattern, nextColumn, startTime, endTime);
} }
@ -172,10 +171,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern(); var pattern = new Pattern();
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < AvailableColumns) if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
{ {
while (PreviousPattern.ColumnHasObject(nextColumn)) while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, AvailableColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
} }
int lastColumn = nextColumn; int lastColumn = nextColumn;
@ -183,7 +182,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{ {
addToPattern(pattern, nextColumn, startTime, startTime); addToPattern(pattern, nextColumn, startTime, startTime);
while (nextColumn == lastColumn) while (nextColumn == lastColumn)
nextColumn = Random.Next(RandomStart, AvailableColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
lastColumn = nextColumn; lastColumn = nextColumn;
startTime += segmentDuration; startTime += segmentDuration;
@ -221,7 +220,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Check if we're at the borders of the stage, and invert the pattern if so // Check if we're at the borders of the stage, and invert the pattern if so
if (increasing) if (increasing)
{ {
if (column >= AvailableColumns - 1) if (column >= TotalColumns - 1)
{ {
increasing = false; increasing = false;
column--; column--;
@ -259,8 +258,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern(); var pattern = new Pattern();
bool legacy = AvailableColumns >= 4 && AvailableColumns <= 8; bool legacy = TotalColumns >= 4 && TotalColumns <= 8;
int interval = Random.Next(1, AvailableColumns - (legacy ? 1 : 0)); int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0));
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i <= repeatCount; i++) for (int i = 0; i <= repeatCount; i++)
@ -268,15 +267,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
addToPattern(pattern, nextColumn, startTime, startTime); addToPattern(pattern, nextColumn, startTime, startTime);
nextColumn += interval; nextColumn += interval;
if (nextColumn >= AvailableColumns - RandomStart) if (nextColumn >= TotalColumns - RandomStart)
nextColumn = nextColumn - AvailableColumns - RandomStart + (legacy ? 1 : 0); nextColumn = nextColumn - TotalColumns - RandomStart + (legacy ? 1 : 0);
nextColumn += RandomStart; nextColumn += RandomStart;
// If we're in 2K, let's not add many consecutive doubles // If we're in 2K, let's not add many consecutive doubles
if (AvailableColumns > 2) if (TotalColumns > 2)
addToPattern(pattern, nextColumn, startTime, startTime); addToPattern(pattern, nextColumn, startTime, startTime);
nextColumn = Random.Next(RandomStart, AvailableColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
startTime += segmentDuration; startTime += segmentDuration;
} }
@ -298,7 +297,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// □ - □ □ // □ - □ □
// ■ - ■ ■ // ■ - ■ ■
switch (AvailableColumns) switch (TotalColumns)
{ {
case 2: case 2:
p2 = 0; p2 = 0;
@ -351,19 +350,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern(); var pattern = new Pattern();
int columnRepeat = Math.Min(repeatCount, AvailableColumns); int columnRepeat = Math.Min(repeatCount, TotalColumns);
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < AvailableColumns) if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
{ {
while (PreviousPattern.ColumnHasObject(nextColumn)) while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, AvailableColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
} }
for (int i = 0; i < columnRepeat; i++) for (int i = 0; i < columnRepeat; i++)
{ {
while (pattern.ColumnHasObject(nextColumn)) while (pattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, AvailableColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(pattern, nextColumn, startTime, endTime); addToPattern(pattern, nextColumn, startTime, endTime);
startTime += segmentDuration; startTime += segmentDuration;
@ -388,10 +387,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern(); var pattern = new Pattern();
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < AvailableColumns) if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
{ {
while (PreviousPattern.ColumnHasObject(holdColumn)) while (PreviousPattern.ColumnHasObject(holdColumn))
holdColumn = Random.Next(RandomStart, AvailableColumns); holdColumn = Random.Next(RandomStart, TotalColumns);
} }
// Create the hold note // Create the hold note
@ -401,13 +400,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (ConversionDifficulty > 6.5) if (ConversionDifficulty > 6.5)
noteCount = GetRandomNoteCount(0.63, 0); noteCount = GetRandomNoteCount(0.63, 0);
else if (ConversionDifficulty > 4) else if (ConversionDifficulty > 4)
noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0.12 : 0.45, 0); noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0);
else if (ConversionDifficulty > 2.5) else if (ConversionDifficulty > 2.5)
noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0 : 0.24, 0); noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0);
noteCount = Math.Min(AvailableColumns - 1, noteCount); noteCount = Math.Min(TotalColumns - 1, noteCount);
bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
int nextColumn = Random.Next(RandomStart, AvailableColumns); int nextColumn = Random.Next(RandomStart, TotalColumns);
var rowPattern = new Pattern(); var rowPattern = new Pattern();
for (int i = 0; i <= repeatCount; i++) for (int i = 0; i <= repeatCount; i++)
@ -417,7 +416,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
for (int j = 0; j < noteCount; j++) for (int j = 0; j < noteCount; j++)
{ {
while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn) while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn)
nextColumn = Random.Next(RandomStart, AvailableColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(rowPattern, nextColumn, startTime, startTime); addToPattern(rowPattern, nextColumn, startTime, startTime);
} }
} }

View File

@ -2,7 +2,6 @@
// 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.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
@ -16,8 +15,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{ {
private readonly double endTime; private readonly double endTime;
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns) public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap)
: base(random, hitObject, beatmap, availableColumns, new Pattern()) : base(random, hitObject, beatmap, new Pattern())
{ {
var endtimeData = HitObject as IHasEndTime; var endtimeData = HitObject as IHasEndTime;
@ -30,14 +29,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool generateHold = endTime - HitObject.StartTime >= 100; bool generateHold = endTime - HitObject.StartTime >= 100;
if (AvailableColumns == 8) if (TotalColumns == 8)
{ {
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000) if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000)
addToPattern(pattern, 0, generateHold); addToPattern(pattern, 0, generateHold);
else else
addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold); addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold);
} }
else if (AvailableColumns > 0) else if (TotalColumns > 0)
addToPattern(pattern, getNextRandomColumn(0), generateHold); addToPattern(pattern, getNextRandomColumn(0), generateHold);
return pattern; return pattern;
@ -50,10 +49,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <returns>A random column after <paramref name="start"/>.</returns> /// <returns>A random column after <paramref name="start"/>.</returns>
private int getNextRandomColumn(int start) private int getNextRandomColumn(int start)
{ {
int nextColumn = Random.Next(start, AvailableColumns); int nextColumn = Random.Next(start, TotalColumns);
while (PreviousPattern.ColumnHasObject(nextColumn)) while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(start, AvailableColumns); nextColumn = Random.Next(start, TotalColumns);
return nextColumn; return nextColumn;
} }

View File

@ -5,7 +5,6 @@ using System;
using System.Linq; using System.Linq;
using OpenTK; using OpenTK;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
@ -20,8 +19,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType; private readonly PatternType convertType;
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair) public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
: base(random, hitObject, beatmap, availableColumns, previousPattern) : base(random, hitObject, beatmap, previousPattern)
{ {
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime)); if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density)); if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
@ -88,23 +87,23 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Generate a new pattern by copying the last hit objects in reverse-column order // Generate a new pattern by copying the last hit objects in reverse-column order
var pattern = new Pattern(); var pattern = new Pattern();
for (int i = RandomStart; i < AvailableColumns; i++) for (int i = RandomStart; i < TotalColumns; i++)
if (PreviousPattern.ColumnHasObject(i)) if (PreviousPattern.ColumnHasObject(i))
addToPattern(pattern, RandomStart + AvailableColumns - i - 1); addToPattern(pattern, RandomStart + TotalColumns - i - 1);
return pattern; return pattern;
} }
if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1 if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
// If we convert to 7K + 1, let's not overload the special key // If we convert to 7K + 1, let's not overload the special key
&& (AvailableColumns != 8 || lastColumn != 0) && (TotalColumns != 8 || lastColumn != 0)
// Make sure the last column was not the centre column // Make sure the last column was not the centre column
&& (AvailableColumns % 2 == 0 || lastColumn != AvailableColumns / 2)) && (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
{ {
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object) // Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
var pattern = new Pattern(); var pattern = new Pattern();
int column = RandomStart + AvailableColumns - lastColumn - 1; int column = RandomStart + TotalColumns - lastColumn - 1;
addToPattern(pattern, column); addToPattern(pattern, column);
return pattern; return pattern;
@ -115,7 +114,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Generate a new pattern by placing on the already filled columns // Generate a new pattern by placing on the already filled columns
var pattern = new Pattern(); var pattern = new Pattern();
for (int i = RandomStart; i < AvailableColumns; i++) for (int i = RandomStart; i < TotalColumns; i++)
if (PreviousPattern.ColumnHasObject(i)) if (PreviousPattern.ColumnHasObject(i))
addToPattern(pattern, i); addToPattern(pattern, i);
@ -128,7 +127,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern(); var pattern = new Pattern();
int targetColumn = lastColumn + 1; int targetColumn = lastColumn + 1;
if (targetColumn == AvailableColumns) if (targetColumn == TotalColumns)
{ {
targetColumn = RandomStart; targetColumn = RandomStart;
StairType = PatternType.ReverseStair; StairType = PatternType.ReverseStair;
@ -146,7 +145,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int targetColumn = lastColumn - 1; int targetColumn = lastColumn - 1;
if (targetColumn == RandomStart - 1) if (targetColumn == RandomStart - 1)
{ {
targetColumn = AvailableColumns - 1; targetColumn = TotalColumns - 1;
StairType = PatternType.Stair; StairType = PatternType.Stair;
} }
@ -206,7 +205,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool allowStacking = (convertType & PatternType.ForceNotStack) == 0; bool allowStacking = (convertType & PatternType.ForceNotStack) == 0;
if (!allowStacking) if (!allowStacking)
noteCount = Math.Min(noteCount, AvailableColumns - RandomStart - PreviousPattern.ColumnWithObjects); noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i < noteCount; i++) for (int i = 0; i < noteCount; i++)
@ -216,11 +215,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if ((convertType & PatternType.Gathered) > 0) if ((convertType & PatternType.Gathered) > 0)
{ {
nextColumn++; nextColumn++;
if (nextColumn == AvailableColumns) if (nextColumn == TotalColumns)
nextColumn = RandomStart; nextColumn = RandomStart;
} }
else else
nextColumn = Random.Next(RandomStart, AvailableColumns); nextColumn = Random.Next(RandomStart, TotalColumns);
} }
addToPattern(pattern, nextColumn); addToPattern(pattern, nextColumn);
@ -268,7 +267,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool addToCentre; bool addToCentre;
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre); int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre);
int columnLimit = (AvailableColumns % 2 == 0 ? AvailableColumns : AvailableColumns - 1) / 2; int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
int nextColumn = Random.Next(RandomStart, columnLimit); int nextColumn = Random.Next(RandomStart, columnLimit);
for (int i = 0; i < noteCount; i++) for (int i = 0; i < noteCount; i++)
{ {
@ -278,11 +277,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Add normal note // Add normal note
addToPattern(pattern, nextColumn); addToPattern(pattern, nextColumn);
// Add mirrored note // Add mirrored note
addToPattern(pattern, RandomStart + AvailableColumns - nextColumn - 1); addToPattern(pattern, RandomStart + TotalColumns - nextColumn - 1);
} }
if (addToCentre) if (addToCentre)
addToPattern(pattern, AvailableColumns / 2); addToPattern(pattern, TotalColumns / 2);
if (RandomStart > 0 && hasSpecialColumn) if (RandomStart > 0 && hasSpecialColumn)
addToPattern(pattern, 0); addToPattern(pattern, 0);
@ -300,7 +299,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <returns>The amount of notes to be generated.</returns> /// <returns>The amount of notes to be generated.</returns>
private int getRandomNoteCount(double p2, double p3, double p4, double p5) private int getRandomNoteCount(double p2, double p3, double p4, double p5)
{ {
switch (AvailableColumns) switch (TotalColumns)
{ {
case 2: case 2:
p2 = 0; p2 = 0;
@ -348,7 +347,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if ((convertType & PatternType.ForceNotStack) > 0) if ((convertType & PatternType.ForceNotStack) > 0)
return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3); return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3);
switch (AvailableColumns) switch (TotalColumns)
{ {
case 2: case 2:
centreProbability = 0; centreProbability = 0;
@ -379,7 +378,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
double centreVal = Random.NextDouble(); double centreVal = Random.NextDouble();
int noteCount = GetRandomNoteCount(p2, p3); int noteCount = GetRandomNoteCount(p2, p3);
addToCentre = AvailableColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability; addToCentre = TotalColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability;
return noteCount; return noteCount;
} }

View File

@ -25,16 +25,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// </summary> /// </summary>
protected readonly FastRandom Random; protected readonly FastRandom Random;
protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern) protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
: base(hitObject, beatmap, availableColumns, previousPattern) : base(hitObject, beatmap, previousPattern)
{ {
if (random == null) throw new ArgumentNullException(nameof(random)); if (random == null) throw new ArgumentNullException(nameof(random));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern)); if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
Random = random; Random = random;
RandomStart = AvailableColumns == 8 ? 1 : 0; RandomStart = TotalColumns == 8 ? 1 : 0;
} }
/// <summary> /// <summary>
@ -45,14 +44,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <returns>The column.</returns> /// <returns>The column.</returns>
protected int GetColumn(float position, bool allowSpecial = false) protected int GetColumn(float position, bool allowSpecial = false)
{ {
if (allowSpecial && AvailableColumns == 8) if (allowSpecial && TotalColumns == 8)
{ {
const float local_x_divisor = 512f / 7; const float local_x_divisor = 512f / 7;
return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1; return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1;
} }
float localXDivisor = 512f / AvailableColumns; float localXDivisor = 512f / TotalColumns;
return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, AvailableColumns - 1); return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, TotalColumns - 1);
} }
/// <summary> /// <summary>

View File

@ -2,7 +2,6 @@
// 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 osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
@ -12,11 +11,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
/// </summary> /// </summary>
internal abstract class PatternGenerator internal abstract class PatternGenerator
{ {
/// <summary>
/// The number of columns available to create the pattern.
/// </summary>
protected readonly int AvailableColumns;
/// <summary> /// <summary>
/// The last pattern. /// The last pattern.
/// </summary> /// </summary>
@ -30,19 +24,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
/// <summary> /// <summary>
/// The beatmap which <see cref="HitObject"/> is a part of. /// The beatmap which <see cref="HitObject"/> is a part of.
/// </summary> /// </summary>
protected readonly Beatmap Beatmap; protected readonly ManiaBeatmap Beatmap;
protected PatternGenerator(HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern) protected readonly int TotalColumns;
protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
{ {
if (hitObject == null) throw new ArgumentNullException(nameof(hitObject)); if (hitObject == null) throw new ArgumentNullException(nameof(hitObject));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern)); if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
HitObject = hitObject; HitObject = hitObject;
Beatmap = beatmap; Beatmap = beatmap;
AvailableColumns = availableColumns;
PreviousPattern = previousPattern; PreviousPattern = previousPattern;
TotalColumns = Beatmap.TotalColumns;
} }
/// <summary> /// <summary>

View File

@ -0,0 +1,18 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Beatmaps
{
/// <summary>
/// Defines properties for each stage in a <see cref="ManiaPlayfield"/>.
/// </summary>
public struct StageDefinition
{
/// <summary>
/// The number of <see cref="Column"/>s which this stage contains.
/// </summary>
public int Columns;
}
}

View File

@ -5,7 +5,6 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using System.Collections.Generic; using System.Collections.Generic;
using System;
namespace osu.Game.Rulesets.Mania namespace osu.Game.Rulesets.Mania
{ {
@ -18,6 +17,6 @@ namespace osu.Game.Rulesets.Mania
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0; public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize))); protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap);
} }
} }

View File

@ -8,6 +8,7 @@ using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
@ -96,21 +97,30 @@ namespace osu.Game.Rulesets.Mania.Mods
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer) public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{ {
int availableColumns = ((ManiaRulesetContainer)rulesetContainer).AvailableColumns; int availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns;
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList(); var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]); rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
} }
} }
public abstract class ManiaKeyMod : Mod public abstract class ManiaKeyMod : Mod, IApplicableMod, IApplicableToBeatmapConverter<ManiaHitObject>
{ {
// TODO: implement using the IApplicable interface. Haven't done so yet because KeyCount isn't even hooked up at the moment.
public override string ShortenedName => Name; public override string ShortenedName => Name;
public abstract int KeyCount { get; } public abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true; public override bool Ranked => true;
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 = KeyCount;
}
} }
public class ManiaModKey1 : ManiaKeyMod public class ManiaModKey1 : ManiaKeyMod

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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 System.Linq; using System.Linq;
using OpenTK; using OpenTK;
@ -28,11 +27,7 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject> public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject>
{ {
/// <summary> public new ManiaBeatmap Beatmap => (ManiaBeatmap)base.Beatmap;
/// The number of columns which the <see cref="ManiaPlayfield"/> should display, and which
/// the beatmap converter will attempt to convert beatmaps to use.
/// </summary>
public int AvailableColumns { get; private set; }
public IEnumerable<DrawableBarLine> BarLines; public IEnumerable<DrawableBarLine> BarLines;
@ -73,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.UI
BarLines.ForEach(Playfield.Add); BarLines.ForEach(Playfield.Add);
} }
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(AvailableColumns) protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.TotalColumns)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -81,27 +76,9 @@ 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, AvailableColumns); public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Beatmap.TotalColumns);
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap);
{
if (IsForCurrentRuleset)
AvailableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.CircleSize));
else
{
float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count;
if (percentSliderOrSpinner < 0.2)
AvailableColumns = 7;
else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.CircleSize) >= 5)
AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) > 5 ? 7 : 6;
else if (percentSliderOrSpinner > 0.6)
AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) > 4 ? 5 : 4;
else
AvailableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) + 1, 7));
}
return new ManiaBeatmapConverter(IsForCurrentRuleset, AvailableColumns);
}
protected override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h) protected override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h)
{ {

View File

@ -48,6 +48,8 @@
<Reference Include="System.Core" /> <Reference Include="System.Core" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\StageDefinition.cs" />
<Compile Include="Beatmaps\ManiaBeatmap.cs" />
<Compile Include="Beatmaps\Patterns\Legacy\EndTimeObjectPatternGenerator.cs" /> <Compile Include="Beatmaps\Patterns\Legacy\EndTimeObjectPatternGenerator.cs" />
<Compile Include="Beatmaps\Patterns\Legacy\DistanceObjectPatternGenerator.cs" /> <Compile Include="Beatmaps\Patterns\Legacy\DistanceObjectPatternGenerator.cs" />
<Compile Include="Beatmaps\Patterns\Legacy\PatternGenerator.cs" /> <Compile Include="Beatmaps\Patterns\Legacy\PatternGenerator.cs" />

View File

@ -158,14 +158,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateCurrentState(ArmedState state) protected override void UpdateCurrentState(ArmedState state)
{ {
Ball.FadeIn(); Ball.FadeIn();
Ball.ScaleTo(HitObject.Scale);
using (BeginDelayedSequence(slider.Duration, true)) using (BeginDelayedSequence(slider.Duration, true))
{ {
Body.FadeOut(160); const float fade_out_time = 450;
Ball.FadeOut(160);
this.FadeOut(800) // intentionally pile on an extra FadeOut to make it happen much faster.
.Expire(); Ball.FadeOut(fade_out_time / 4, Easing.Out);
switch (state)
{
case ArmedState.Hit:
Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out);
break;
}
this.FadeOut(fade_out_time, Easing.OutQuint).Expire();
} }
} }

View File

@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
} }
private readonly Slider slider; private readonly Slider slider;
private readonly Box follow; public readonly Box FollowCircle;
private readonly Box ball; private readonly Box ball;
public SliderBall(Slider slider) public SliderBall(Slider slider)
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Children = new Drawable[] Children = new Drawable[]
{ {
follow = new Box FollowCircle = new Box
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
@ -101,11 +101,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
// If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position. // If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos); public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos);
public override void ClearTransforms(bool propagateChildren = false, string targetMember = null) public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
{ {
// Consider the case of rewinding - children's transforms are handled internally, so propagating down // Consider the case of rewinding - children's transforms are handled internally, so propagating down
// any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point. // any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point.
base.ClearTransforms(false, targetMember); base.ClearTransformsAfter(time, false, targetMember);
} }
private bool tracking; private bool tracking;
@ -118,8 +118,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return; return;
tracking = value; tracking = value;
follow.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint); FollowCircle.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
follow.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint); FollowCircle.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint);
} }
} }
@ -129,12 +129,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
base.Update(); base.Update();
if (Time.Current < slider.EndTime)
{
// Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position. // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
Tracking = canCurrentlyTrack Tracking = canCurrentlyTrack
&& lastState != null && lastState != null
&& base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
&& ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
} }
}
public void UpdateProgress(double progress, int repeat) public void UpdateProgress(double progress, int repeat)
{ {

View File

@ -39,12 +39,12 @@ namespace osu.Game.Beatmaps
/// <returns>The converted Beatmap.</returns> /// <returns>The converted Beatmap.</returns>
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original) protected virtual Beatmap<T> ConvertBeatmap(Beatmap original)
{ {
return new Beatmap<T> var beatmap = CreateBeatmap();
{ beatmap.BeatmapInfo = original.BeatmapInfo;
BeatmapInfo = original.BeatmapInfo, beatmap.ControlPointInfo = original.ControlPointInfo;
ControlPointInfo = original.ControlPointInfo, beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList();
HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList()
}; return beatmap;
} }
/// <summary> /// <summary>
@ -78,6 +78,11 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
protected abstract IEnumerable<Type> ValidConversionTypes { get; } protected abstract IEnumerable<Type> ValidConversionTypes { get; }
/// <summary>
/// Creates the <see cref="Beatmap{T}"/> that will be returned by this <see cref="BeatmapProcessor{T}"/>.
/// </summary>
protected virtual Beatmap<T> CreateBeatmap() => new Beatmap<T>();
/// <summary> /// <summary>
/// Performs the conversion of a hit object. /// Performs the conversion of a hit object.
/// This method is generally executed sequentially for all objects in a beatmap. /// This method is generally executed sequentially for all objects in a beatmap.

View File

@ -64,24 +64,24 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <returns>The timing control point.</returns> /// <returns>The timing control point.</returns>
public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault()); public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault());
[JsonIgnore]
/// <summary> /// <summary>
/// Finds the maximum BPM represented by any timing control point. /// Finds the maximum BPM represented by any timing control point.
/// </summary> /// </summary>
[JsonIgnore]
public double BPMMaximum => public double BPMMaximum =>
60000 / (TimingPoints.OrderBy(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; 60000 / (TimingPoints.OrderBy(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength;
[JsonIgnore]
/// <summary> /// <summary>
/// Finds the minimum BPM represented by any timing control point. /// Finds the minimum BPM represented by any timing control point.
/// </summary> /// </summary>
[JsonIgnore]
public double BPMMinimum => public double BPMMinimum =>
60000 / (TimingPoints.OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength; 60000 / (TimingPoints.OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength;
[JsonIgnore]
/// <summary> /// <summary>
/// Finds the mode BPM (most common BPM) represented by the control points. /// Finds the mode BPM (most common BPM) represented by the control points.
/// </summary> /// </summary>
[JsonIgnore]
public double BPMMode => public double BPMMode =>
60000 / (TimingPoints.GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).FirstOrDefault()?.FirstOrDefault() ?? new TimingControlPoint()).BeatLength; 60000 / (TimingPoints.GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).FirstOrDefault()?.FirstOrDefault() ?? new TimingControlPoint()).BeatLength;

View File

@ -73,8 +73,6 @@ namespace osu.Game.Graphics.UserInterface
}); });
} }
private bool pending;
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{ {
if ((invalidation & Invalidation.DrawSize) > 0) if ((invalidation & Invalidation.DrawSize) > 0)

View File

@ -9,11 +9,11 @@ using Newtonsoft.Json.Linq;
namespace osu.Game.IO.Serialization.Converters namespace osu.Game.IO.Serialization.Converters
{ {
/// <summary> /// <summary>
/// A type of <see cref="JsonConverter"/> that serializes a <see cref="List<T>"/> alongside /// A type of <see cref="JsonConverter"/> that serializes a <see cref="List{T}"/> alongside
/// a lookup table for the types contained. The lookup table is used in deserialization to /// a lookup table for the types contained. The lookup table is used in deserialization to
/// reconstruct the objects with their original types. /// reconstruct the objects with their original types.
/// </summary> /// </summary>
/// <typeparam name="T">The type of objects contained in the <see cref="List<T>"/> this attribute is attached to.</typeparam> /// <typeparam name="T">The type of objects contained in the <see cref="List{T}"/> this attribute is attached to.</typeparam>
public class TypedListConverter<T> : JsonConverter public class TypedListConverter<T> : JsonConverter
{ {
private readonly bool requiresTypeVersion; private readonly bool requiresTypeVersion;

View File

@ -4,7 +4,6 @@
using OpenTK; using OpenTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
@ -16,7 +15,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{ {
public abstract class DrawableProfileScore : DrawableProfileRow public abstract class DrawableProfileScore : DrawableProfileRow
{ {
private readonly FillFlowContainer metadata;
private readonly ScoreModsContainer modsContainer; private readonly ScoreModsContainer modsContainer;
protected readonly Score Score; protected readonly Score Score;

View File

@ -0,0 +1,22 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// Interface for a <see cref="Mod"/> that applies changes to a <see cref="BeatmapConverter{TObject}"/>.
/// </summary>
/// <typeparam name="TObject">The type of converted <see cref="HitObject"/>.</typeparam>
public interface IApplicableToBeatmapConverter<TObject>
where TObject : HitObject
{
/// <summary>
/// Applies this <see cref="Mod"/> to a <see cref="BeatmapConverter{TObject}"/>.
/// </summary>
/// <param name="beatmapConverter">The <see cref="BeatmapConverter{TObject}"/> to apply to.</param>
void ApplyToBeatmapConverter(BeatmapConverter<TObject> beatmapConverter);
}
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods
/// <summary> /// <summary>
/// Applies this <see cref="IApplicableToDrawableHitObjects"/> to a list of <see cref="DrawableHitObject"/>s. /// Applies this <see cref="IApplicableToDrawableHitObjects"/> to a list of <see cref="DrawableHitObject"/>s.
/// </summary> /// </summary>
/// <param name="drawable">The list of <see cref="DrawableHitObject"/>s to apply to.</param> /// <param name="drawables">The list of <see cref="DrawableHitObject"/>s to apply to.</param>
void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables); void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables);
} }
} }

View File

@ -176,6 +176,10 @@ namespace osu.Game.Rulesets.UI
if (!converter.CanConvert(workingBeatmap.Beatmap)) if (!converter.CanConvert(workingBeatmap.Beatmap))
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter})."); throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter}).");
// Apply conversion adjustments before converting
foreach (var mod in Mods.OfType<IApplicableToBeatmapConverter<TObject>>())
mod.ApplyToBeatmapConverter(converter);
// Convert the beatmap // Convert the beatmap
Beatmap = converter.Convert(workingBeatmap.Beatmap); Beatmap = converter.Convert(workingBeatmap.Beatmap);

View File

@ -101,8 +101,6 @@ namespace osu.Game.Screens
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE));
} }
private Shader currentLoadTarget;
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();

View File

@ -20,6 +20,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Localisation;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select
{ {
@ -86,6 +87,8 @@ namespace osu.Game.Screens.Select
public OsuSpriteText ArtistLabel { get; private set; } public OsuSpriteText ArtistLabel { get; private set; }
public FillFlowContainer MapperContainer { get; private set; } public FillFlowContainer MapperContainer { get; private set; }
public FillFlowContainer InfoLabelContainer { get; private set; } public FillFlowContainer InfoLabelContainer { get; private set; }
private UnicodeBindableString titleBinding;
private UnicodeBindableString artistBinding;
public BufferedWedgeInfo(WorkingBeatmap working) public BufferedWedgeInfo(WorkingBeatmap working)
{ {
@ -93,7 +96,7 @@ namespace osu.Game.Screens.Select
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load(LocalisationEngine localisation)
{ {
var beatmapInfo = working.BeatmapInfo; var beatmapInfo = working.BeatmapInfo;
var metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); var metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
@ -102,6 +105,9 @@ namespace osu.Game.Screens.Select
CacheDrawnFrameBuffer = true; CacheDrawnFrameBuffer = true;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
titleBinding = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title);
artistBinding = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist);
Children = new Drawable[] Children = new Drawable[]
{ {
// We will create the white-to-black gradient by modulating transparency and having // We will create the white-to-black gradient by modulating transparency and having
@ -167,13 +173,11 @@ namespace osu.Game.Screens.Select
TitleLabel = new OsuSpriteText TitleLabel = new OsuSpriteText
{ {
Font = @"Exo2.0-MediumItalic", Font = @"Exo2.0-MediumItalic",
Text = string.IsNullOrEmpty(metadata.Source) ? metadata.Title : metadata.Source + " — " + metadata.Title,
TextSize = 28, TextSize = 28,
}, },
ArtistLabel = new OsuSpriteText ArtistLabel = new OsuSpriteText
{ {
Font = @"Exo2.0-MediumItalic", Font = @"Exo2.0-MediumItalic",
Text = metadata.Artist,
TextSize = 17, TextSize = 17,
}, },
MapperContainer = new FillFlowContainer MapperContainer = new FillFlowContainer
@ -193,6 +197,15 @@ namespace osu.Game.Screens.Select
} }
} }
}; };
artistBinding.ValueChanged += value => setMetadata(metadata.Source);
artistBinding.TriggerChange();
}
private void setMetadata(string source)
{
ArtistLabel.Text = artistBinding.Value;
TitleLabel.Text = string.IsNullOrEmpty(source) ? titleBinding.Value : source + " — " + titleBinding.Value;
ForceRedraw();
} }
private InfoLabel[] getInfoLabels() private InfoLabel[] getInfoLabels()

View File

@ -10,8 +10,6 @@ namespace osu.Game.Screens.Select.Carousel
/// </summary> /// </summary>
public class CarouselGroup : CarouselItem public class CarouselGroup : CarouselItem
{ {
private readonly List<CarouselItem> items;
protected override DrawableCarouselItem CreateDrawableRepresentation() => null; protected override DrawableCarouselItem CreateDrawableRepresentation() => null;
public IReadOnlyList<CarouselItem> Children => InternalChildren; public IReadOnlyList<CarouselItem> Children => InternalChildren;

View File

@ -31,8 +31,6 @@ namespace osu.Game.Screens.Select.Carousel
private readonly BeatmapSetInfo beatmapSet; private readonly BeatmapSetInfo beatmapSet;
private readonly FillFlowContainer difficultyIcons;
public DrawableCarouselBeatmapSet(CarouselBeatmapSet set) public DrawableCarouselBeatmapSet(CarouselBeatmapSet set)
: base(set) : base(set)
{ {

View File

@ -40,7 +40,7 @@ namespace osu.Game.Screens.Select.Leaderboards
{ {
private readonly SpriteIcon icon; private readonly SpriteIcon icon;
public Action Action; public new Action Action;
public RetryButton() public RetryButton()
{ {

View File

@ -313,6 +313,7 @@
<Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" /> <Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" />
<Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" /> <Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
<Compile Include="Rulesets\Mods\IApplicableMod.cs" /> <Compile Include="Rulesets\Mods\IApplicableMod.cs" />
<Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />
<Compile Include="Rulesets\Mods\IApplicableToDrawableHitObject.cs" /> <Compile Include="Rulesets\Mods\IApplicableToDrawableHitObject.cs" />
<Compile Include="Rulesets\UI\Scrolling\ScrollingDirection.cs" /> <Compile Include="Rulesets\UI\Scrolling\ScrollingDirection.cs" />
<Compile Include="Rulesets\UI\Scrolling\ScrollingHitObjectContainer.cs" /> <Compile Include="Rulesets\UI\Scrolling\ScrollingHitObjectContainer.cs" />