mirror of
https://github.com/ppy/osu.git
synced 2026-05-14 06:42:34 +08:00
Compare commits
488 Commits
@@ -24,7 +24,9 @@ Clone the repository including submodules
|
||||
Build and run
|
||||
|
||||
- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included)
|
||||
- From command line using `dotnet run --project osu.Desktop`
|
||||
- From command line using `dotnet run --project osu.Desktop`. When building for non-development purposes, add `-c Release` to gain higher performance.
|
||||
|
||||
Note: If you run from command line under linux, you will need to prefix the output folder to your `LD_LIBRARY_PATH`. See `.vscode/launch.json` for an example
|
||||
|
||||
If you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`). Visual Studio Code users must run `Restore` task from debug tab before attempt to build.
|
||||
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ before_build:
|
||||
- cmd: nuget restore -verbosity quiet
|
||||
build_script:
|
||||
- ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1'))
|
||||
- appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate
|
||||
- cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx
|
||||
- appveyor DownloadFile https://puu.sh/BCrS8/7faccf7876.enc # signing certificate
|
||||
- cmd: appveyor-tools\secure-file -decrypt 7faccf7876.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx
|
||||
- appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration
|
||||
- cd osu-deploy
|
||||
- nuget restore -verbosity quiet
|
||||
|
||||
@@ -27,9 +27,6 @@ namespace osu.Desktop.Overlays
|
||||
private NotificationOverlay notificationOverlay;
|
||||
private GameHost host;
|
||||
|
||||
public override bool HandleKeyboardInput => false;
|
||||
public override bool HandleMouseInput => false;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config, GameHost host)
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace osu.Desktop
|
||||
{
|
||||
bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0;
|
||||
|
||||
Logger.Log($"Unhandled exception has been {(continueExecution ? "allowed with {allowableExceptions} more allowable exceptions" : "denied")} .");
|
||||
Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} .");
|
||||
|
||||
// restore the stock of allowable exceptions after a short delay.
|
||||
Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions));
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
|
||||
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.1" />
|
||||
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
<EmbeddedResource Include="lazer.ico" />
|
||||
|
||||
@@ -74,42 +74,42 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
|
||||
private void initialiseHyperDash(List<CatchHitObject> objects)
|
||||
{
|
||||
// todo: add difficulty adjust.
|
||||
double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2;
|
||||
List<CatchHitObject> objectWithDroplets = new List<CatchHitObject>();
|
||||
|
||||
foreach (var currentObject in objects)
|
||||
{
|
||||
if (currentObject is Fruit)
|
||||
objectWithDroplets.Add(currentObject);
|
||||
if (currentObject is JuiceStream)
|
||||
foreach (var currentJuiceElement in currentObject.NestedHitObjects)
|
||||
if (!(currentJuiceElement is TinyDroplet))
|
||||
objectWithDroplets.Add((CatchHitObject)currentJuiceElement);
|
||||
}
|
||||
|
||||
objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
|
||||
|
||||
double halfCatcherWidth = CatcherArea.GetCatcherSize(Beatmap.BeatmapInfo.BaseDifficulty) / 2;
|
||||
int lastDirection = 0;
|
||||
double lastExcess = halfCatcherWidth;
|
||||
|
||||
int objCount = objects.Count;
|
||||
|
||||
for (int i = 0; i < objCount - 1; i++)
|
||||
for (int i = 0; i < objectWithDroplets.Count - 1; i++)
|
||||
{
|
||||
CatchHitObject currentObject = objects[i];
|
||||
|
||||
// not needed?
|
||||
// if (currentObject is TinyDroplet) continue;
|
||||
|
||||
CatchHitObject nextObject = objects[i + 1];
|
||||
|
||||
// while (nextObject is TinyDroplet)
|
||||
// {
|
||||
// if (++i == objCount - 1) break;
|
||||
// nextObject = objects[i + 1];
|
||||
// }
|
||||
CatchHitObject currentObject = objectWithDroplets[i];
|
||||
CatchHitObject nextObject = objectWithDroplets[i + 1];
|
||||
|
||||
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
|
||||
double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4;
|
||||
double timeToNext = nextObject.StartTime - currentObject.StartTime;
|
||||
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
||||
|
||||
if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext)
|
||||
float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext);
|
||||
if (distanceToHyper < 0)
|
||||
{
|
||||
currentObject.HyperDashTarget = nextObject;
|
||||
lastExcess = halfCatcherWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
//currentObject.DistanceToHyperDash = timeToNext - distanceToNext;
|
||||
lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth);
|
||||
currentObject.DistanceToHyperDash = distanceToHyper;
|
||||
lastExcess = MathHelper.Clamp(distanceToHyper, 0, halfCatcherWidth);
|
||||
}
|
||||
|
||||
lastDirection = thisDirection;
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
public int IndexInBeatmap { get; set; }
|
||||
|
||||
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
|
||||
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(ComboIndex % 4);
|
||||
|
||||
public virtual bool NewCombo { get; set; }
|
||||
|
||||
@@ -27,7 +27,9 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
public int ComboIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The distance for a fruit to to next hyper if it's not a hyper.
|
||||
/// Difference between the distance to the next object
|
||||
/// and the distance that would have triggered a hyper dash.
|
||||
/// A value close to 0 indicates a difficult jump (for difficulty calculation).
|
||||
/// </summary>
|
||||
public float DistanceToHyperDash { get; set; }
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@@ -22,6 +23,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
private readonly CatcherArea catcherArea;
|
||||
|
||||
protected override bool UserScrollSpeedAdjustment => false;
|
||||
|
||||
protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Constant;
|
||||
|
||||
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
|
||||
: base(BASE_WIDTH)
|
||||
{
|
||||
@@ -53,6 +58,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
});
|
||||
|
||||
VisibleTimeRange.Value = BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
||||
}
|
||||
|
||||
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
|
||||
|
||||
@@ -107,6 +107,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
|
||||
|
||||
public static float GetCatcherSize(BeatmapDifficulty difficulty)
|
||||
{
|
||||
return CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||
}
|
||||
|
||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||
{
|
||||
/// <summary>
|
||||
@@ -407,9 +412,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// </summary>
|
||||
public void Explode()
|
||||
{
|
||||
var fruit = caughtFruit.ToArray();
|
||||
|
||||
foreach (var f in fruit)
|
||||
foreach (var f in caughtFruit.ToArray())
|
||||
Explode(f);
|
||||
}
|
||||
|
||||
@@ -422,15 +425,15 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
fruit.Anchor = Anchor.TopLeft;
|
||||
fruit.Position = caughtFruit.ToSpaceOfOtherDrawable(fruit.DrawPosition, ExplodingFruitTarget);
|
||||
|
||||
caughtFruit.Remove(fruit);
|
||||
if (!caughtFruit.Remove(fruit))
|
||||
// we may have already been removed by a previous operation (due to the weird OnLoadComplete scheduling).
|
||||
// this avoids a crash on potentially attempting to Add a fruit to ExplodingFruitTarget twice.
|
||||
return;
|
||||
|
||||
ExplodingFruitTarget.Add(fruit);
|
||||
}
|
||||
|
||||
fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine)
|
||||
.Then()
|
||||
.MoveToY(fruit.Y + 50, 500, Easing.InSine);
|
||||
|
||||
fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine).Then().MoveToY(fruit.Y + 50, 500, Easing.InSine);
|
||||
fruit.MoveToX(fruit.X + originalX * 6, 1000);
|
||||
fruit.FadeOut(750);
|
||||
|
||||
|
||||
+11
-47
@@ -173,26 +173,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
var pattern = new Pattern();
|
||||
|
||||
int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects;
|
||||
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
int nextColumn = GetRandomColumn();
|
||||
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
|
||||
{
|
||||
// Find available column
|
||||
RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn), () =>
|
||||
{
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
});
|
||||
|
||||
nextColumn = FindAvailableColumn(nextColumn, pattern, PreviousPattern);
|
||||
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||
}
|
||||
|
||||
// This is can't be combined with the above loop due to RNG
|
||||
for (int i = 0; i < noteCount - usableColumns; i++)
|
||||
{
|
||||
RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
|
||||
{
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
});
|
||||
|
||||
nextColumn = FindAvailableColumn(nextColumn, pattern);
|
||||
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||
}
|
||||
|
||||
@@ -217,23 +209,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
{
|
||||
RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
|
||||
{
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
});
|
||||
}
|
||||
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
|
||||
|
||||
int lastColumn = nextColumn;
|
||||
for (int i = 0; i < noteCount; i++)
|
||||
{
|
||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||
|
||||
RunWhile(() => nextColumn == lastColumn, () =>
|
||||
{
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
});
|
||||
|
||||
nextColumn = FindAvailableColumn(nextColumn, validation: c => c != lastColumn);
|
||||
lastColumn = nextColumn;
|
||||
startTime += SegmentDuration;
|
||||
}
|
||||
@@ -325,7 +307,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (TotalColumns > 2)
|
||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
nextColumn = GetRandomColumn();
|
||||
startTime += SegmentDuration;
|
||||
}
|
||||
|
||||
@@ -404,20 +386,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
{
|
||||
RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
|
||||
{
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
});
|
||||
}
|
||||
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
|
||||
|
||||
for (int i = 0; i < columnRepeat; i++)
|
||||
{
|
||||
RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
|
||||
{
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
});
|
||||
|
||||
nextColumn = FindAvailableColumn(nextColumn, pattern);
|
||||
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||
startTime += SegmentDuration;
|
||||
}
|
||||
@@ -442,17 +415,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
{
|
||||
RunWhile(() => PreviousPattern.ColumnHasObject(holdColumn), () =>
|
||||
{
|
||||
holdColumn = Random.Next(RandomStart, TotalColumns);
|
||||
});
|
||||
}
|
||||
holdColumn = FindAvailableColumn(holdColumn, PreviousPattern);
|
||||
|
||||
// Create the hold note
|
||||
addToPattern(pattern, holdColumn, startTime, EndTime);
|
||||
|
||||
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
int nextColumn = GetRandomColumn();
|
||||
int noteCount;
|
||||
if (ConversionDifficulty > 6.5)
|
||||
noteCount = GetRandomNoteCount(0.63, 0);
|
||||
@@ -473,11 +441,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
for (int j = 0; j < noteCount; j++)
|
||||
{
|
||||
RunWhile(() => rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn, () =>
|
||||
{
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
});
|
||||
|
||||
nextColumn = FindAvailableColumn(nextColumn, validation: c => c != holdColumn, patterns: rowPattern);
|
||||
addToPattern(rowPattern, nextColumn, startTime, startTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,34 +39,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
addToPattern(pattern, 0, generateHold);
|
||||
break;
|
||||
case 8:
|
||||
addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold);
|
||||
addToPattern(pattern, FindAvailableColumn(GetRandomColumn(), PreviousPattern), generateHold);
|
||||
break;
|
||||
default:
|
||||
if (TotalColumns > 0)
|
||||
addToPattern(pattern, getNextRandomColumn(0), generateHold);
|
||||
addToPattern(pattern, GetRandomColumn(), generateHold);
|
||||
break;
|
||||
}
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Picks a random column after a column.
|
||||
/// </summary>
|
||||
/// <param name="start">The starting column.</param>
|
||||
/// <returns>A random column after <paramref name="start"/>.</returns>
|
||||
private int getNextRandomColumn(int start)
|
||||
{
|
||||
int nextColumn = Random.Next(start, TotalColumns);
|
||||
|
||||
RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
|
||||
{
|
||||
nextColumn = Random.Next(start, TotalColumns);
|
||||
});
|
||||
|
||||
return nextColumn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and adds a note to a pattern.
|
||||
/// </summary>
|
||||
|
||||
@@ -25,9 +25,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
PatternType lastStair, IBeatmap originalBeatmap)
|
||||
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
|
||||
{
|
||||
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
|
||||
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
|
||||
|
||||
StairType = lastStair;
|
||||
|
||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||
@@ -234,22 +231,27 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
for (int i = 0; i < noteCount; i++)
|
||||
{
|
||||
RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking, () =>
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.Gathered))
|
||||
{
|
||||
nextColumn++;
|
||||
if (nextColumn == TotalColumns)
|
||||
nextColumn = RandomStart;
|
||||
}
|
||||
else
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
});
|
||||
nextColumn = allowStacking
|
||||
? FindAvailableColumn(nextColumn, nextColumn: getNextColumn, patterns: pattern)
|
||||
: FindAvailableColumn(nextColumn, nextColumn: getNextColumn, patterns: new[] { pattern, PreviousPattern });
|
||||
|
||||
addToPattern(pattern, nextColumn);
|
||||
}
|
||||
|
||||
return pattern;
|
||||
|
||||
int getNextColumn(int last)
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.Gathered))
|
||||
{
|
||||
last++;
|
||||
if (last == TotalColumns)
|
||||
last = RandomStart;
|
||||
}
|
||||
else
|
||||
last = GetRandomColumn();
|
||||
return last;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -295,13 +297,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre);
|
||||
|
||||
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
|
||||
int nextColumn = Random.Next(RandomStart, columnLimit);
|
||||
int nextColumn = GetRandomColumn(upperBound: columnLimit);
|
||||
for (int i = 0; i < noteCount; i++)
|
||||
{
|
||||
RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
|
||||
{
|
||||
nextColumn = Random.Next(RandomStart, columnLimit);
|
||||
});
|
||||
nextColumn = FindAvailableColumn(nextColumn, upperBound: columnLimit, patterns: pattern);
|
||||
|
||||
// Add normal note
|
||||
addToPattern(pattern, nextColumn);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.MathUtils;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@@ -90,6 +91,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
}
|
||||
|
||||
private double? conversionDifficulty;
|
||||
|
||||
/// <summary>
|
||||
/// A difficulty factor used for various conversion methods from osu!stable.
|
||||
/// </summary>
|
||||
@@ -116,5 +118,82 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
return conversionDifficulty.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a new column in which a <see cref="HitObject"/> can be placed.
|
||||
/// This uses <see cref="GetRandomColumn"/> to pick the next candidate column.
|
||||
/// </summary>
|
||||
/// <param name="initialColumn">The initial column to test. This may be returned if it is already a valid column.</param>
|
||||
/// <param name="patterns">A list of patterns for which the validity of a column should be checked against.
|
||||
/// A column is not a valid candidate if a <see cref="HitObject"/> occupies the same column in any of the patterns.</param>
|
||||
/// <returns>A column for which there are no <see cref="HitObject"/>s in any of <paramref name="patterns"/> occupying the same column.</returns>
|
||||
/// <exception cref="NotEnoughColumnsException">If there are no valid candidate columns.</exception>
|
||||
protected int FindAvailableColumn(int initialColumn, params Pattern[] patterns)
|
||||
=> FindAvailableColumn(initialColumn, null, patterns: patterns);
|
||||
|
||||
/// <summary>
|
||||
/// Finds a new column in which a <see cref="HitObject"/> can be placed.
|
||||
/// </summary>
|
||||
/// <param name="initialColumn">The initial column to test. This may be returned if it is already a valid column.</param>
|
||||
/// <param name="nextColumn">A function to retrieve the next column. If null, a randomisation scheme will be used.</param>
|
||||
/// <param name="validation">A function to perform additional validation checks to determine if a column is a valid candidate for a <see cref="HitObject"/>.</param>
|
||||
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
|
||||
/// <param name="upperBound">The maximum column index. If null, <see cref="PatternGenerator.TotalColumns"/> is used.</param>
|
||||
/// <param name="patterns">A list of patterns for which the validity of a column should be checked against.
|
||||
/// A column is not a valid candidate if a <see cref="HitObject"/> occupies the same column in any of the patterns.</param>
|
||||
/// <returns>A column which has passed the <paramref name="validation"/> check and for which there are no
|
||||
/// <see cref="HitObject"/>s in any of <paramref name="patterns"/> occupying the same column.</returns>
|
||||
/// <exception cref="NotEnoughColumnsException">If there are no valid candidate columns.</exception>
|
||||
protected int FindAvailableColumn(int initialColumn, int? lowerBound = null, int? upperBound = null, Func<int, int> nextColumn = null, [InstantHandle] Func<int, bool> validation = null,
|
||||
params Pattern[] patterns)
|
||||
{
|
||||
lowerBound = lowerBound ?? RandomStart;
|
||||
upperBound = upperBound ?? TotalColumns;
|
||||
nextColumn = nextColumn ?? (_ => GetRandomColumn(lowerBound, upperBound));
|
||||
|
||||
// Check for the initial column
|
||||
if (isValid(initialColumn))
|
||||
return initialColumn;
|
||||
|
||||
// Ensure that we have at least one free column, so that an endless loop is avoided
|
||||
bool hasValidColumns = false;
|
||||
for (int i = lowerBound.Value; i < upperBound.Value; i++)
|
||||
{
|
||||
hasValidColumns = isValid(i);
|
||||
if (hasValidColumns)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasValidColumns)
|
||||
throw new NotEnoughColumnsException();
|
||||
|
||||
// Iterate until a valid column is found. This is a random iteration in the default case.
|
||||
do
|
||||
{
|
||||
initialColumn = nextColumn(initialColumn);
|
||||
} while (!isValid(initialColumn));
|
||||
|
||||
return initialColumn;
|
||||
|
||||
bool isValid(int column) => validation?.Invoke(column) != false && !patterns.Any(p => p.ColumnHasObject(column));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random column index in the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
|
||||
/// </summary>
|
||||
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
|
||||
/// <param name="upperBound">The maximum column index. If null, <see cref="PatternGenerator.TotalColumns"/> is used.</param>
|
||||
protected int GetRandomColumn(int? lowerBound = null, int? upperBound = null) => Random.Next(lowerBound ?? RandomStart, upperBound ?? TotalColumns);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when mania conversion is stuck in an infinite loop unable to find columns to place new hitobjects in.
|
||||
/// </summary>
|
||||
public class NotEnoughColumnsException : Exception
|
||||
{
|
||||
public NotEnoughColumnsException()
|
||||
: base("There were not enough columns to complete conversion.")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
||||
@@ -15,14 +12,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
||||
/// </summary>
|
||||
internal abstract class PatternGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// An arbitrary maximum amount of iterations to perform in <see cref="RunWhile"/>.
|
||||
/// The specific value is not super important - enough such that no false-positives occur.
|
||||
///
|
||||
/// /b/933228 requires at least 23 iterations.
|
||||
/// </summary>
|
||||
private const int max_rng_iterations = 30;
|
||||
|
||||
/// <summary>
|
||||
/// The last pattern.
|
||||
/// </summary>
|
||||
@@ -53,44 +42,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
||||
TotalColumns = Beatmap.TotalColumns;
|
||||
}
|
||||
|
||||
protected void RunWhile([InstantHandle] Func<bool> condition, Action action)
|
||||
{
|
||||
int iterations = 0;
|
||||
|
||||
while (condition())
|
||||
{
|
||||
if (iterations++ >= max_rng_iterations)
|
||||
{
|
||||
// log an error but don't throw. we want to continue execution.
|
||||
Logger.Error(new ExceededAllowedIterationsException(new StackTrace(0)),
|
||||
"Conversion encountered errors. The beatmap may not be correctly converted.");
|
||||
return;
|
||||
}
|
||||
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the patterns for <see cref="HitObject"/>, each filled with hit objects.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="Pattern"/>s containing the hit objects.</returns>
|
||||
public abstract IEnumerable<Pattern> Generate();
|
||||
|
||||
/// <summary>
|
||||
/// Denotes when a single conversion operation is in an infinitely looping state.
|
||||
/// </summary>
|
||||
public class ExceededAllowedIterationsException : Exception
|
||||
{
|
||||
private readonly string stackTrace;
|
||||
|
||||
public ExceededAllowedIterationsException(StackTrace stackTrace)
|
||||
{
|
||||
this.stackTrace = stackTrace.ToString();
|
||||
}
|
||||
|
||||
public override string StackTrace => stackTrace;
|
||||
public override string ToString() => $"{GetType().Name}: {Message}\r\n{StackTrace}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
|
||||
}
|
||||
|
||||
// Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input.
|
||||
public override bool HandleMouseInput => false;
|
||||
public override bool HandlePositionalInput => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
|
||||
}
|
||||
|
||||
protected void BeginHold()
|
||||
{
|
||||
holdStartTime = Time.Current;
|
||||
bodyPiece.Hitting = true;
|
||||
}
|
||||
|
||||
protected void EndHold()
|
||||
{
|
||||
holdStartTime = null;
|
||||
bodyPiece.Hitting = false;
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
{
|
||||
// Make sure the action happened within the body of the hold note
|
||||
@@ -123,8 +135,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
|
||||
// and within the limited range of the above if-statement. This state will be managed by the head note if the
|
||||
// user has pressed during the hit windows of the head note.
|
||||
holdStartTime = Time.Current;
|
||||
|
||||
BeginHold();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -137,7 +148,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (action != Action.Value)
|
||||
return false;
|
||||
|
||||
holdStartTime = null;
|
||||
EndHold();
|
||||
|
||||
// If the key has been released too early, the user should not receive full score for the release
|
||||
if (!Tail.IsHit)
|
||||
@@ -170,7 +181,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
|
||||
// The body doesn't handle these early early hits, so we have to explicitly set the holding state here
|
||||
holdNote.holdStartTime = Time.Current;
|
||||
holdNote.BeginHold();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
background = new Box { RelativeSizeAxes = Axes.Both },
|
||||
foreground = new BufferedContainer
|
||||
{
|
||||
Blending = BlendingMode.Additive,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CacheDrawnFrameBuffer = true,
|
||||
Children = new Drawable[]
|
||||
@@ -73,6 +74,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
}
|
||||
|
||||
private Color4 accentColour;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get { return accentColour; }
|
||||
@@ -86,6 +88,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
}
|
||||
}
|
||||
|
||||
public bool Hitting
|
||||
{
|
||||
get { return hitting; }
|
||||
set
|
||||
{
|
||||
hitting = value;
|
||||
updateAccentColour();
|
||||
}
|
||||
}
|
||||
|
||||
private Cached subtractionCache = new Cached();
|
||||
|
||||
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
||||
@@ -118,13 +130,26 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
}
|
||||
}
|
||||
|
||||
private bool hitting;
|
||||
|
||||
private void updateAccentColour()
|
||||
{
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
foreground.Colour = AccentColour.Opacity(0.9f);
|
||||
background.Colour = AccentColour.Opacity(0.6f);
|
||||
foreground.Colour = AccentColour.Opacity(0.5f);
|
||||
background.Colour = AccentColour.Opacity(0.7f);
|
||||
|
||||
const float animation_length = 50;
|
||||
|
||||
foreground.ClearTransforms(false, nameof(foreground.Colour));
|
||||
if (hitting)
|
||||
{
|
||||
// wait for the next sync point
|
||||
double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2);
|
||||
using (foreground.BeginDelayedSequence(synchronisedOffset))
|
||||
foreground.FadeColour(AccentColour.Lighten(0.2f), animation_length).Then().FadeColour(foreground.Colour, animation_length).Loop();
|
||||
}
|
||||
|
||||
subtractionCache.Invalidate();
|
||||
}
|
||||
|
||||
@@ -30,10 +30,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
if (Result.IsHit)
|
||||
{
|
||||
this.ScaleTo(0.8f);
|
||||
this.ScaleTo(1, 250, Easing.OutElastic);
|
||||
JudgementBody.ScaleTo(0.8f);
|
||||
JudgementBody.ScaleTo(1, 250, Easing.OutElastic);
|
||||
|
||||
this.Delay(50).FadeOut(200).ScaleTo(0.75f, 250);
|
||||
JudgementBody.Delay(50).ScaleTo(0.75f, 250);
|
||||
this.Delay(50).FadeOut(200);
|
||||
}
|
||||
|
||||
Expire();
|
||||
|
||||
@@ -26,11 +26,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
throw new ArgumentException("Can't have zero or fewer stages.");
|
||||
|
||||
GridContainer playfieldGrid;
|
||||
InternalChild = playfieldGrid = new GridContainer
|
||||
AddInternal(playfieldGrid = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[] { new Drawable[stageDefinitions.Count] }
|
||||
};
|
||||
});
|
||||
|
||||
var normalColumnAction = ManiaAction.Key1;
|
||||
var specialColumnAction = ManiaAction.Special1;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class StackingTest
|
||||
{
|
||||
[Test]
|
||||
public void TestStacking()
|
||||
{
|
||||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(beatmap_data)))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
var objects = converted.HitObjects.ToList();
|
||||
|
||||
// The last hitobject triggers the stacking
|
||||
for (int i = 0; i < objects.Count - 1; i++)
|
||||
Assert.AreEqual(0, ((OsuHitObject)objects[i]).StackHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private const string beatmap_data = @"
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.2
|
||||
|
||||
[Difficulty]
|
||||
ApproachRate:9.2
|
||||
SliderMultiplier:1
|
||||
SliderTickRate:0.5
|
||||
|
||||
[TimingPoints]
|
||||
217871,6400,4,2,1,20,1,0
|
||||
217871,-800,4,2,1,20,0,0
|
||||
218071,-787.5,4,2,1,20,0,0
|
||||
218271,-775,4,2,1,20,0,0
|
||||
218471,-762.5,4,2,1,20,0,0
|
||||
218671,-750,4,2,1,20,0,0
|
||||
240271,-10,4,2,0,5,0,0
|
||||
|
||||
[HitObjects]
|
||||
311,185,217871,6,0,L|318:158,1,25
|
||||
311,185,218071,2,0,L|335:170,1,25
|
||||
311,185,218271,2,0,L|338:192,1,25
|
||||
311,185,218471,2,0,L|325:209,1,25
|
||||
311,185,218671,2,0,L|304:212,1,25
|
||||
311,185,240271,5,0,0:0:0:0:
|
||||
";
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDrawableHitCircle : DrawableHitCircle
|
||||
protected class TestDrawableHitCircle : DrawableHitCircle
|
||||
{
|
||||
private readonly bool auto;
|
||||
|
||||
@@ -94,6 +94,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
this.auto = auto;
|
||||
}
|
||||
|
||||
public void TriggerJudgement() => UpdateResult(true);
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (auto && !userTriggered && timeOffset > 0)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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.Framework.Graphics;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class TestCaseShaking : TestCaseHitCircle
|
||||
{
|
||||
public override void Add(Drawable drawable)
|
||||
{
|
||||
base.Add(drawable);
|
||||
|
||||
if (drawable is TestDrawableHitCircle hitObject)
|
||||
{
|
||||
Scheduler.AddDelayed(() => hitObject.TriggerJudgement(),
|
||||
hitObject.HitObject.StartTime - (hitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,9 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
{
|
||||
}
|
||||
|
||||
public override void PreProcess()
|
||||
public override void PostProcess()
|
||||
{
|
||||
base.PreProcess();
|
||||
base.PostProcess();
|
||||
applyStacking((Beatmap<OsuHitObject>)Beatmap);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||
}
|
||||
|
||||
// Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input.
|
||||
public override bool HandleMouseInput => false;
|
||||
public override bool HandlePositionalInput => false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||
body.UpdateProgress(0);
|
||||
}
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos);
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => body.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition);
|
||||
public override Quad SelectionQuad => body.PathDrawQuad;
|
||||
|
||||
@@ -2,14 +2,88 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModRelax : ModRelax
|
||||
public class OsuModRelax : ModRelax, IApplicableFailOverride, IUpdatableByPlayfield, IApplicableToRulesetContainer<OsuHitObject>
|
||||
{
|
||||
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
|
||||
|
||||
public bool AllowFail => false;
|
||||
|
||||
public void Update(Playfield playfield)
|
||||
{
|
||||
bool requiresHold = false;
|
||||
bool requiresHit = false;
|
||||
|
||||
const float relax_leniency = 3;
|
||||
|
||||
foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
|
||||
{
|
||||
if (!(drawable is DrawableOsuHitObject osuHit))
|
||||
continue;
|
||||
|
||||
double time = osuHit.Clock.CurrentTime;
|
||||
double relativetime = time - osuHit.HitObject.StartTime;
|
||||
|
||||
if (time < osuHit.HitObject.StartTime - relax_leniency) continue;
|
||||
|
||||
if (osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime || osuHit.IsHit)
|
||||
continue;
|
||||
|
||||
requiresHit |= osuHit is DrawableHitCircle && osuHit.IsHovered && osuHit.HitObject.HitWindows.CanBeHit(relativetime);
|
||||
requiresHold |= osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered) || osuHit is DrawableSpinner;
|
||||
}
|
||||
|
||||
if (requiresHit)
|
||||
{
|
||||
addAction(false);
|
||||
addAction(true);
|
||||
}
|
||||
|
||||
addAction(requiresHold);
|
||||
}
|
||||
|
||||
private bool wasHit;
|
||||
private bool wasLeft;
|
||||
|
||||
private OsuInputManager osuInputManager;
|
||||
|
||||
private void addAction(bool hitting)
|
||||
{
|
||||
if (wasHit == hitting)
|
||||
return;
|
||||
|
||||
wasHit = hitting;
|
||||
|
||||
var state = new ReplayState<OsuAction>
|
||||
{
|
||||
PressedActions = new List<OsuAction>()
|
||||
};
|
||||
|
||||
if (hitting)
|
||||
{
|
||||
state.PressedActions.Add(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
|
||||
wasLeft = !wasLeft;
|
||||
}
|
||||
|
||||
state.Apply(osuInputManager.CurrentState, osuInputManager);
|
||||
}
|
||||
|
||||
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
|
||||
{
|
||||
// grab the input manager for future use.
|
||||
osuInputManager = (OsuInputManager)rulesetContainer.KeyBindingInputManager;
|
||||
osuInputManager.AllowUserPresses = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
internal class OsuModTransform : Mod, IApplicableToDrawableHitObjects
|
||||
{
|
||||
public override string Name => "Transform";
|
||||
public override string ShortenedName => "TR";
|
||||
public override FontAwesome Icon => FontAwesome.fa_arrows;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override string Description => "Everything rotates. EVERYTHING.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle) };
|
||||
|
||||
private float theta;
|
||||
|
||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
{
|
||||
foreach (var drawable in drawables)
|
||||
{
|
||||
var hitObject = (OsuHitObject) drawable.HitObject;
|
||||
|
||||
float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2;
|
||||
|
||||
Vector2 originalPosition = drawable.Position;
|
||||
Vector2 appearOffset = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * appearDistance;
|
||||
|
||||
//the - 1 and + 1 prevents the hit objects to appear in the wrong position.
|
||||
double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1;
|
||||
double moveDuration = hitObject.TimePreempt + 1;
|
||||
|
||||
using (drawable.BeginAbsoluteSequence(appearTime, true))
|
||||
{
|
||||
drawable
|
||||
.MoveToOffset(appearOffset)
|
||||
.MoveTo(originalPosition, moveDuration, Easing.InOutSine);
|
||||
}
|
||||
|
||||
theta += (float) hitObject.TimeFadeIn / 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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 osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
internal class OsuModWiggle : Mod, IApplicableToDrawableHitObjects
|
||||
{
|
||||
public override string Name => "Wiggle";
|
||||
public override string ShortenedName => "WG";
|
||||
public override FontAwesome Icon => FontAwesome.fa_certificate;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override string Description => "They just won't stay still...";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform) };
|
||||
|
||||
private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles
|
||||
private const int wiggle_strength = 10; // Higher = stronger wiggles
|
||||
|
||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
{
|
||||
foreach (var drawable in drawables)
|
||||
drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState;
|
||||
}
|
||||
|
||||
private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state)
|
||||
{
|
||||
var osuObject = (OsuHitObject)drawable.HitObject;
|
||||
Vector2 origin = drawable.Position;
|
||||
|
||||
Random objRand = new Random((int)osuObject.StartTime);
|
||||
|
||||
// Wiggle all objects during TimePreempt
|
||||
int amountWiggles = (int)osuObject.TimePreempt / wiggle_duration;
|
||||
|
||||
void wiggle()
|
||||
{
|
||||
float nextAngle = (float)(objRand.NextDouble() * 2 * Math.PI);
|
||||
float nextDist = (float)(objRand.NextDouble() * wiggle_strength);
|
||||
drawable.MoveTo(new Vector2((float)(nextDist * Math.Cos(nextAngle) + origin.X), (float)(nextDist * Math.Sin(nextAngle) + origin.Y)), wiggle_duration);
|
||||
}
|
||||
|
||||
for (int i = 0; i < amountWiggles; i++)
|
||||
using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration, true))
|
||||
wiggle();
|
||||
|
||||
// Keep wiggling sliders and spinners for their duration
|
||||
if (!(osuObject is IHasEndTime endTime))
|
||||
return;
|
||||
|
||||
amountWiggles = (int)(endTime.Duration / wiggle_duration);
|
||||
|
||||
for (int i = 0; i < amountWiggles; i++)
|
||||
using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration, true))
|
||||
wiggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||
if (result == HitResult.None)
|
||||
{
|
||||
Shake(Math.Abs(timeOffset) - HitObject.HitWindows.HalfWindowFor(HitResult.Miss));
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyResult(r => r.Type = result);
|
||||
}
|
||||
|
||||
@@ -10,19 +10,29 @@ using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject>
|
||||
{
|
||||
public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - HitObject.TimePreempt;
|
||||
public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Clock?.CurrentTime >= HitObject.StartTime - HitObject.TimePreempt;
|
||||
|
||||
private readonly ShakeContainer shakeContainer;
|
||||
|
||||
protected DrawableOsuHitObject(OsuHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
base.AddInternal(shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both });
|
||||
Alpha = 0;
|
||||
}
|
||||
|
||||
// Forward all internal management to shakeContainer.
|
||||
// This is a bit ugly but we don't have the concept of InternalContent so it'll have to do for now. (https://github.com/ppy/osu-framework/issues/1690)
|
||||
protected override void AddInternal(Drawable drawable) => shakeContainer.Add(drawable);
|
||||
protected override void ClearInternal(bool disposeChildren = true) => shakeContainer.Clear(disposeChildren);
|
||||
protected override bool RemoveInternal(Drawable drawable) => shakeContainer.Remove(drawable);
|
||||
|
||||
protected sealed override void UpdateState(ArmedState state)
|
||||
{
|
||||
double transformTime = HitObject.StartTime - HitObject.TimePreempt;
|
||||
@@ -68,6 +78,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
private OsuInputManager osuActionInputManager;
|
||||
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
|
||||
|
||||
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
|
||||
|
||||
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,14 +44,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
},
|
||||
ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
||||
repeatPoints = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both },
|
||||
Ball = new SliderBall(s)
|
||||
Ball = new SliderBall(s, this)
|
||||
{
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(s.Scale),
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0
|
||||
},
|
||||
HeadCircle = new DrawableSliderHead(s, s.HeadCircle),
|
||||
HeadCircle = new DrawableSliderHead(s, s.HeadCircle)
|
||||
{
|
||||
OnShake = Shake
|
||||
},
|
||||
TailCircle = new DrawableSliderTail(s, s.TailCircle)
|
||||
};
|
||||
|
||||
@@ -181,6 +184,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos);
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Body.ReceivePositionalInputAt(screenSpacePos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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 osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
|
||||
@@ -28,5 +29,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
if (!IsHit)
|
||||
Position = slider.CurvePositionAt(completionProgress);
|
||||
}
|
||||
|
||||
public Action<double> OnShake;
|
||||
|
||||
protected override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class CirclePiece : Container, IKeyBindingHandler<OsuAction>
|
||||
{
|
||||
// IsHovered is used
|
||||
public override bool HandlePositionalInput => true;
|
||||
|
||||
public Func<bool> Hit;
|
||||
|
||||
public CirclePiece()
|
||||
|
||||
@@ -5,12 +5,11 @@ using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.EventArgs;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
@@ -37,9 +36,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
private readonly Slider slider;
|
||||
public readonly Drawable FollowCircle;
|
||||
private Drawable drawableBall;
|
||||
private readonly DrawableSlider drawableSlider;
|
||||
|
||||
public SliderBall(Slider slider)
|
||||
public SliderBall(Slider slider, DrawableSlider drawableSlider = null)
|
||||
{
|
||||
this.drawableSlider = drawableSlider;
|
||||
this.slider = slider;
|
||||
Masking = true;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
@@ -101,29 +102,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
};
|
||||
}
|
||||
|
||||
private InputState lastState;
|
||||
private Vector2? lastScreenSpaceMousePosition;
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
lastState = state;
|
||||
return base.OnMouseDown(state, args);
|
||||
lastScreenSpaceMousePosition = e.ScreenSpaceMousePosition;
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
protected override bool OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
lastState = state;
|
||||
return base.OnMouseUp(state, args);
|
||||
lastScreenSpaceMousePosition = e.ScreenSpaceMousePosition;
|
||||
return base.OnMouseUp(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
lastState = state;
|
||||
return base.OnMouseMove(state);
|
||||
lastScreenSpaceMousePosition = e.ScreenSpaceMousePosition;
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
// 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 void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
|
||||
{
|
||||
// Consider the case of rewinding - children's transforms are handled internally, so propagating down
|
||||
@@ -155,11 +153,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
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 ReceivePositionalInputAt so that we correctly check the position.
|
||||
Tracking = canCurrentlyTrack
|
||||
&& lastState != null
|
||||
&& base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
|
||||
&& ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
|
||||
&& lastScreenSpaceMousePosition.HasValue
|
||||
&& ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value)
|
||||
&& (drawableSlider?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,15 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Graphics.Lines;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics.ES30;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
@@ -43,6 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
public double? SnakedEnd { get; private set; }
|
||||
|
||||
private Color4 accentColour = Color4.White;
|
||||
|
||||
/// <summary>
|
||||
/// Used to colour the path.
|
||||
/// </summary>
|
||||
@@ -61,6 +63,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
}
|
||||
|
||||
private Color4 borderColour = Color4.White;
|
||||
|
||||
/// <summary>
|
||||
/// Used to colour the path border.
|
||||
/// </summary>
|
||||
@@ -85,6 +88,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
private Vector2 topLeftOffset;
|
||||
|
||||
private readonly Slider slider;
|
||||
|
||||
public SliderBody(Slider s)
|
||||
{
|
||||
slider = s;
|
||||
@@ -108,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
container.Attach(RenderbufferInternalFormat.DepthComponent16);
|
||||
}
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => path.ReceiveMouseInputAt(screenSpacePos);
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
public void SetRange(double p0, double p1)
|
||||
{
|
||||
@@ -139,8 +143,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
var texture = new Texture(textureWidth, 1);
|
||||
|
||||
//initialise background
|
||||
var raw = new RawTexture(textureWidth, 1);
|
||||
var bytes = raw.Data;
|
||||
var raw = new Image<Rgba32>(textureWidth, 1);
|
||||
|
||||
const float aa_portion = 0.02f;
|
||||
const float border_portion = 0.128f;
|
||||
@@ -155,19 +158,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
if (progress <= border_portion)
|
||||
{
|
||||
bytes[i * 4] = (byte)(BorderColour.R * 255);
|
||||
bytes[i * 4 + 1] = (byte)(BorderColour.G * 255);
|
||||
bytes[i * 4 + 2] = (byte)(BorderColour.B * 255);
|
||||
bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (BorderColour.A * 255));
|
||||
raw[i, 0] = new Rgba32(BorderColour.R, BorderColour.G, BorderColour.B, Math.Min(progress / aa_portion, 1) * BorderColour.A);
|
||||
}
|
||||
else
|
||||
{
|
||||
progress -= border_portion;
|
||||
|
||||
bytes[i * 4] = (byte)(AccentColour.R * 255);
|
||||
bytes[i * 4 + 1] = (byte)(AccentColour.G * 255);
|
||||
bytes[i * 4 + 2] = (byte)(AccentColour.B * 255);
|
||||
bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (AccentColour.A * 255));
|
||||
raw[i, 0] = new Rgba32(AccentColour.R, AccentColour.G, AccentColour.B,
|
||||
(opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * AccentColour.A);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class SpinnerBackground : CircularContainer, IHasAccentColour
|
||||
{
|
||||
public override bool HandleKeyboardInput => false;
|
||||
public override bool HandleMouseInput => false;
|
||||
|
||||
protected Box Disc;
|
||||
|
||||
public Color4 AccentColour
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
};
|
||||
}
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
private bool tracking;
|
||||
public bool Tracking
|
||||
@@ -68,10 +68,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position);
|
||||
return base.OnMouseMove(state);
|
||||
mousePosition = Parent.ToLocalSpace(e.ScreenSpaceMousePosition);
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
private Vector2 mousePosition;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu
|
||||
@@ -12,8 +13,33 @@ namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
public IEnumerable<OsuAction> PressedActions => KeyBindingContainer.PressedActions;
|
||||
|
||||
public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique)
|
||||
public bool AllowUserPresses
|
||||
{
|
||||
set => ((OsuKeyBindingContainer)KeyBindingContainer).AllowUserPresses = value;
|
||||
}
|
||||
|
||||
protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
=> new OsuKeyBindingContainer(ruleset, variant, unique);
|
||||
|
||||
public OsuInputManager(RulesetInfo ruleset)
|
||||
: base(ruleset, 0, SimultaneousBindingMode.Unique)
|
||||
{
|
||||
}
|
||||
|
||||
private class OsuKeyBindingContainer : RulesetKeyBindingContainer
|
||||
{
|
||||
public bool AllowUserPresses = true;
|
||||
|
||||
public OsuKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
: base(ruleset, variant, unique)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool Handle(UIEvent e)
|
||||
{
|
||||
if (!AllowUserPresses) return false;
|
||||
return base.Handle(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +47,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
[Description("Left Button")]
|
||||
LeftButton,
|
||||
|
||||
[Description("Right Button")]
|
||||
RightButton
|
||||
}
|
||||
|
||||
@@ -117,6 +117,11 @@ namespace osu.Game.Rulesets.Osu
|
||||
new OsuModRelax(),
|
||||
new OsuModAutopilot(),
|
||||
};
|
||||
case ModType.Fun:
|
||||
return new Mod[] {
|
||||
new OsuModTransform(),
|
||||
new OsuModWiggle(),
|
||||
};
|
||||
default:
|
||||
return new Mod[] { };
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shaders;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
@@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ShaderManager shaders, TextureStore textures)
|
||||
@@ -117,15 +117,15 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
timeOffset = Time.Current;
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
Vector2 pos = state.Mouse.NativeState.Position;
|
||||
Vector2 pos = e.ScreenSpaceMousePosition;
|
||||
|
||||
if (lastPosition == null)
|
||||
{
|
||||
lastPosition = pos;
|
||||
resampler.AddPosition(lastPosition.Value);
|
||||
return base.OnMouseMove(state);
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
foreach (Vector2 pos2 in resampler.AddPosition(pos))
|
||||
@@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnMouseMove(state);
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
private void addPosition(Vector2 pos)
|
||||
@@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
|
||||
Texture.DrawQuad(
|
||||
new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y),
|
||||
DrawInfo.Colour,
|
||||
DrawColourInfo.Colour,
|
||||
null,
|
||||
v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.
|
||||
public override bool HandlePositionalInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
|
||||
public override void PostProcess()
|
||||
{
|
||||
connectionLayer.HitObjects = HitObjects.Objects.Select(d => d.HitObject).OfType<OsuHitObject>();
|
||||
connectionLayer.HitObjects = HitObjectContainer.Objects.Select(d => d.HitObject).OfType<OsuHitObject>();
|
||||
}
|
||||
|
||||
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
@@ -72,7 +72,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
DrawableOsuJudgement explosion = new DrawableOsuJudgement(result, judgedObject)
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition
|
||||
Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition,
|
||||
Scale = new Vector2(((OsuHitObject)judgedObject.HitObject).Scale * 1.65f)
|
||||
};
|
||||
|
||||
judgementLayer.Add(explosion);
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>
|
||||
{
|
||||
TaikoHitObject first = x.First();
|
||||
if (x.Skip(1).Any())
|
||||
if (x.Skip(1).Any() && !(first is Swell))
|
||||
first.IsStrong = true;
|
||||
return first;
|
||||
}).ToList();
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
|
||||
private bool validActionPressed;
|
||||
|
||||
private bool pressHandledThisFrame;
|
||||
|
||||
protected DrawableHit(Hit hit)
|
||||
: base(hit)
|
||||
{
|
||||
@@ -51,6 +53,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
|
||||
public override bool OnPressed(TaikoAction action)
|
||||
{
|
||||
if (pressHandledThisFrame)
|
||||
return true;
|
||||
|
||||
if (Judged)
|
||||
return false;
|
||||
|
||||
@@ -62,6 +67,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
if (IsHit)
|
||||
HitAction = action;
|
||||
|
||||
// Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded
|
||||
// E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note
|
||||
pressHandledThisFrame = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -76,6 +85,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
base.Update();
|
||||
|
||||
// The input manager processes all input prior to us updating, so this is the perfect time
|
||||
// for us to remove the extra press blocking, before input is handled in the next frame
|
||||
pressHandledThisFrame = false;
|
||||
|
||||
Size = BaseSize * Parent.RelativeChildSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
switch (Result.Type)
|
||||
{
|
||||
case HitResult.Good:
|
||||
Colour = colours.GreenLight;
|
||||
JudgementBody.Colour = colours.GreenLight;
|
||||
break;
|
||||
case HitResult.Great:
|
||||
Colour = colours.BlueLight;
|
||||
JudgementBody.Colour = colours.BlueLight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
// 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.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
@@ -40,6 +41,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
protected override bool UserScrollSpeedAdjustment => false;
|
||||
|
||||
protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Overlapping;
|
||||
|
||||
private readonly Container<HitExplosion> hitExplosionContainer;
|
||||
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
|
||||
private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer;
|
||||
|
||||
@@ -7,7 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@@ -184,7 +184,7 @@ namespace osu.Game.Tests.Visual
|
||||
/// </summary>
|
||||
/// <param name="cursorContainer">The cursor to check.</param>
|
||||
private bool checkAtMouse(CursorContainer cursorContainer)
|
||||
=> Precision.AlmostEquals(InputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition));
|
||||
=> Precision.AlmostEquals(InputManager.CurrentState.Mouse.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition));
|
||||
|
||||
private class CustomCursorBox : Container, IProvideCursor
|
||||
{
|
||||
@@ -193,7 +193,7 @@ namespace osu.Game.Tests.Visual
|
||||
public CursorContainer Cursor { get; }
|
||||
public bool ProvidingUserCursor { get; }
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => base.ReceiveMouseInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor;
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor;
|
||||
|
||||
private readonly Box background;
|
||||
|
||||
@@ -224,16 +224,16 @@ namespace osu.Game.Tests.Visual
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
background.FadeTo(0.4f, 250, Easing.OutQuint);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
background.FadeTo(0.1f, 250);
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,14 @@ using System.Linq;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.EventArgs;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Screens.Play;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Description("player pause/fail screens")]
|
||||
public class TestCaseGameplayMenuOverlay : OsuTestCase
|
||||
public class TestCaseGameplayMenuOverlay : ManualInputManagerTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) };
|
||||
|
||||
@@ -73,12 +73,18 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
AddStep("Show overlay", () => failOverlay.Show());
|
||||
|
||||
AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnMouseMove(null));
|
||||
AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First()));
|
||||
AddStep("Hide overlay", () => failOverlay.Hide());
|
||||
|
||||
AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected));
|
||||
}
|
||||
|
||||
private void press(Key key)
|
||||
{
|
||||
InputManager.PressKey(key);
|
||||
InputManager.ReleaseKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred.
|
||||
/// </summary>
|
||||
@@ -86,7 +92,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
AddStep("Press enter", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter }));
|
||||
AddStep("Press enter", () => press(Key.Enter));
|
||||
AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible);
|
||||
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
@@ -99,7 +105,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
|
||||
AddStep("Up arrow", () => press(Key.Up));
|
||||
AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected);
|
||||
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
@@ -112,7 +118,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddStep("Down arrow", () => press(Key.Down));
|
||||
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
|
||||
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
@@ -125,11 +131,11 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
AddStep("Show overlay", () => failOverlay.Show());
|
||||
|
||||
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
|
||||
AddStep("Up arrow", () => press(Key.Up));
|
||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
|
||||
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
|
||||
AddStep("Up arrow", () => press(Key.Up));
|
||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
|
||||
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
|
||||
AddStep("Up arrow", () => press(Key.Up));
|
||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
|
||||
|
||||
AddStep("Hide overlay", () => failOverlay.Hide());
|
||||
@@ -142,11 +148,11 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
AddStep("Show overlay", () => failOverlay.Show());
|
||||
|
||||
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddStep("Down arrow", () => press(Key.Down));
|
||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
|
||||
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddStep("Down arrow", () => press(Key.Down));
|
||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
|
||||
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddStep("Down arrow", () => press(Key.Down));
|
||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
|
||||
|
||||
AddStep("Hide overlay", () => failOverlay.Hide());
|
||||
@@ -161,8 +167,8 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
var secondButton = pauseOverlay.Buttons.Skip(1).First();
|
||||
|
||||
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null));
|
||||
AddStep("Down arrow", () => press(Key.Down));
|
||||
AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
|
||||
AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected);
|
||||
AddAssert("Second button selected", () => secondButton.Selected);
|
||||
|
||||
@@ -174,12 +180,16 @@ namespace osu.Game.Tests.Visual
|
||||
/// </summary>
|
||||
private void testKeySelectionAfterMouseSelection()
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
AddStep("Show overlay", () =>
|
||||
{
|
||||
pauseOverlay.Show();
|
||||
InputManager.MoveMouseTo(Vector2.Zero);
|
||||
});
|
||||
|
||||
var secondButton = pauseOverlay.Buttons.Skip(1).First();
|
||||
|
||||
AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null));
|
||||
AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
|
||||
AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
|
||||
AddStep("Up arrow", () => press(Key.Up));
|
||||
AddAssert("Second button not selected", () => !secondButton.Selected);
|
||||
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
|
||||
|
||||
@@ -195,9 +205,9 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
var secondButton = pauseOverlay.Buttons.Skip(1).First();
|
||||
|
||||
AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null));
|
||||
AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null));
|
||||
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
|
||||
AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero));
|
||||
AddStep("Down arrow", () => press(Key.Down));
|
||||
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition
|
||||
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
@@ -218,7 +228,7 @@ namespace osu.Game.Tests.Visual
|
||||
var lastAction = pauseOverlay.OnRetry;
|
||||
pauseOverlay.OnRetry = () => triggered = true;
|
||||
|
||||
retryButton.TriggerOnClick();
|
||||
retryButton.Click();
|
||||
pauseOverlay.OnRetry = lastAction;
|
||||
});
|
||||
|
||||
@@ -235,23 +245,28 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
AddStep("Select second button", () =>
|
||||
{
|
||||
pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down });
|
||||
pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down });
|
||||
press(Key.Down);
|
||||
press(Key.Down);
|
||||
});
|
||||
|
||||
var retryButton = pauseOverlay.Buttons.Skip(1).First();
|
||||
|
||||
bool triggered = false;
|
||||
Action lastAction = null;
|
||||
AddStep("Press enter", () =>
|
||||
{
|
||||
var lastAction = pauseOverlay.OnRetry;
|
||||
lastAction = pauseOverlay.OnRetry;
|
||||
pauseOverlay.OnRetry = () => triggered = true;
|
||||
|
||||
retryButton.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter });
|
||||
pauseOverlay.OnRetry = lastAction;
|
||||
press(Key.Enter);
|
||||
});
|
||||
|
||||
AddAssert("Action was triggered", () => triggered);
|
||||
AddAssert("Action was triggered", () =>
|
||||
{
|
||||
if (lastAction != null)
|
||||
{
|
||||
pauseOverlay.OnRetry = lastAction;
|
||||
lastAction = null;
|
||||
}
|
||||
return triggered;
|
||||
});
|
||||
AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,45 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Screens.Play;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseKeyCounter : OsuTestCase
|
||||
public class TestCaseKeyCounter : ManualInputManagerTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(KeyCounterKeyboard),
|
||||
typeof(KeyCounterMouse),
|
||||
typeof(KeyCounterCollection)
|
||||
};
|
||||
|
||||
public TestCaseKeyCounter()
|
||||
{
|
||||
KeyCounterKeyboard rewindTestKeyCounterKeyboard;
|
||||
KeyCounterCollection kc = new KeyCounterCollection
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new KeyCounter[]
|
||||
{
|
||||
new KeyCounterKeyboard(Key.Z),
|
||||
rewindTestKeyCounterKeyboard = new KeyCounterKeyboard(Key.X),
|
||||
new KeyCounterKeyboard(Key.X),
|
||||
new KeyCounterMouse(MouseButton.Left),
|
||||
new KeyCounterMouse(MouseButton.Right),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
AddStep("Add random", () =>
|
||||
{
|
||||
Key key = (Key)((int)Key.A + RNG.Next(26));
|
||||
@@ -34,7 +47,57 @@ namespace osu.Game.Tests.Visual
|
||||
});
|
||||
AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v);
|
||||
|
||||
Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key;
|
||||
double time1 = 0;
|
||||
|
||||
AddStep($"Press {testKey} key", () =>
|
||||
{
|
||||
InputManager.PressKey(testKey);
|
||||
InputManager.ReleaseKey(testKey);
|
||||
});
|
||||
|
||||
AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
|
||||
|
||||
AddStep($"Press {testKey} key", () =>
|
||||
{
|
||||
InputManager.PressKey(testKey);
|
||||
InputManager.ReleaseKey(testKey);
|
||||
time1 = Clock.CurrentTime;
|
||||
});
|
||||
|
||||
AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 2);
|
||||
|
||||
IFrameBasedClock oldClock = null;
|
||||
|
||||
AddStep($"Rewind {testKey} counter once", () =>
|
||||
{
|
||||
oldClock = rewindTestKeyCounterKeyboard.Clock;
|
||||
rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(time1 - 10));
|
||||
});
|
||||
|
||||
AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
|
||||
|
||||
AddStep($"Rewind {testKey} counter to zero", () => rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(0)));
|
||||
|
||||
AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 0);
|
||||
|
||||
AddStep("Restore clock", () => rewindTestKeyCounterKeyboard.Clock = oldClock);
|
||||
|
||||
Add(kc);
|
||||
}
|
||||
|
||||
private class FixedClock : IClock
|
||||
{
|
||||
private readonly double time;
|
||||
|
||||
public FixedClock(double time)
|
||||
{
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public double CurrentTime => time;
|
||||
public double Rate => 1;
|
||||
public bool IsRunning => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddInternal(trackManager);
|
||||
Add(trackManager);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual
|
||||
TestTrackOwner owner = null;
|
||||
PreviewTrack track = null;
|
||||
|
||||
AddStep("get track", () => AddInternal(owner = new TestTrackOwner(track = getTrack())));
|
||||
AddStep("get track", () => Add(owner = new TestTrackOwner(track = getTrack())));
|
||||
AddStep("start", () => track.Start());
|
||||
AddStep("attempt stop", () => trackManager.StopAnyPlaying(this));
|
||||
AddAssert("not stopped", () => track.IsRunning);
|
||||
@@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
var track = getTrack();
|
||||
|
||||
AddInternal(track);
|
||||
Add(track);
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
@@ -27,13 +27,15 @@ namespace osu.Game.Beatmaps
|
||||
[JsonProperty("id")]
|
||||
public int? OnlineBeatmapID
|
||||
{
|
||||
get { return onlineBeatmapID; }
|
||||
set { onlineBeatmapID = value > 0 ? value : null; }
|
||||
get => onlineBeatmapID;
|
||||
set => onlineBeatmapID = value > 0 ? value : null;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public int BeatmapSetInfoID { get; set; }
|
||||
|
||||
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
|
||||
|
||||
[Required]
|
||||
public BeatmapSetInfo BeatmapSet { get; set; }
|
||||
|
||||
@@ -82,7 +84,7 @@ namespace osu.Game.Beatmaps
|
||||
[JsonIgnore]
|
||||
public string StoredBookmarks
|
||||
{
|
||||
get { return string.Join(",", Bookmarks); }
|
||||
get => string.Join(",", Bookmarks);
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
@@ -93,8 +95,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
Bookmarks = value.Split(',').Select(v =>
|
||||
{
|
||||
int val;
|
||||
bool result = int.TryParse(v, out val);
|
||||
bool result = int.TryParse(v, out int val);
|
||||
return new { result, val };
|
||||
}).Where(p => p.result).Select(p => p.val).ToArray();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// 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
|
||||
|
||||
using System;
|
||||
@@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
@@ -62,6 +63,8 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public override string[] HandledExtensions => new[] { ".osz" };
|
||||
|
||||
protected override string ImportFromStablePath => "Songs";
|
||||
|
||||
private readonly RulesetStore rulesets;
|
||||
|
||||
private readonly BeatmapStore beatmaps;
|
||||
@@ -72,11 +75,6 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
|
||||
|
||||
/// <summary>
|
||||
/// Set a storage with access to an osu-stable install for import purposes.
|
||||
/// </summary>
|
||||
public Func<Storage> GetStableStorage { private get; set; }
|
||||
|
||||
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null)
|
||||
: base(storage, contextFactory, new BeatmapStore(contextFactory), importHost)
|
||||
{
|
||||
@@ -103,6 +101,11 @@ namespace osu.Game.Beatmaps
|
||||
b.BeatmapSet = beatmapSet;
|
||||
}
|
||||
|
||||
validateOnlineIds(beatmapSet.Beatmaps);
|
||||
|
||||
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
|
||||
fetchAndPopulateOnlineValues(b, beatmapSet.Beatmaps);
|
||||
|
||||
// check if a set already exists with the same online id, delete if it does.
|
||||
if (beatmapSet.OnlineBeatmapSetID != null)
|
||||
{
|
||||
@@ -114,11 +117,6 @@ namespace osu.Game.Beatmaps
|
||||
Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database);
|
||||
}
|
||||
}
|
||||
|
||||
validateOnlineIds(beatmapSet.Beatmaps);
|
||||
|
||||
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
|
||||
fetchAndPopulateOnlineIDs(b, beatmapSet.Beatmaps);
|
||||
}
|
||||
|
||||
private void validateOnlineIds(List<BeatmapInfo> beatmaps)
|
||||
@@ -195,7 +193,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
downloadNotification.CompletionClickAction = () =>
|
||||
{
|
||||
PresentBeatmap?.Invoke(importedBeatmap);
|
||||
PresentCompletedImport(importedBeatmap.Yield());
|
||||
return true;
|
||||
};
|
||||
downloadNotification.State = ProgressNotificationState.Completed;
|
||||
@@ -231,6 +229,12 @@ namespace osu.Game.Beatmaps
|
||||
BeatmapDownloadBegan?.Invoke(request);
|
||||
}
|
||||
|
||||
protected override void PresentCompletedImport(IEnumerable<BeatmapSetInfo> imported)
|
||||
{
|
||||
base.PresentCompletedImport(imported);
|
||||
PresentBeatmap?.Invoke(imported.LastOrDefault());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an existing download request if it exists.
|
||||
/// </summary>
|
||||
@@ -311,27 +315,6 @@ namespace osu.Game.Beatmaps
|
||||
/// <returns>Results from the provided query.</returns>
|
||||
public IQueryable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
|
||||
|
||||
/// <summary>
|
||||
/// Denotes whether an osu-stable installation is present to perform automated imports from.
|
||||
/// </summary>
|
||||
public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null;
|
||||
|
||||
/// <summary>
|
||||
/// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future.
|
||||
/// </summary>
|
||||
public async Task ImportFromStable()
|
||||
{
|
||||
var stable = GetStableStorage?.Invoke();
|
||||
|
||||
if (stable == null)
|
||||
{
|
||||
Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs").Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a SHA-2 hash from the provided archive based on contained beatmap (.osu) file content.
|
||||
/// </summary>
|
||||
@@ -350,7 +333,11 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
// let's make sure there are actually .osu files to import.
|
||||
string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));
|
||||
if (string.IsNullOrEmpty(mapName)) throw new InvalidOperationException("No beatmap files found in this beatmap archive.");
|
||||
if (string.IsNullOrEmpty(mapName))
|
||||
{
|
||||
Logger.Log($"No beatmap files found in the beatmap archive ({reader.Name}).", LoggingTarget.Database);
|
||||
return null;
|
||||
}
|
||||
|
||||
Beatmap beatmap;
|
||||
using (var stream = new StreamReader(reader.GetStream(mapName)))
|
||||
@@ -401,21 +388,22 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query the API to populate mising OnlineBeatmapID / OnlineBeatmapSetID properties.
|
||||
/// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The beatmap to populate.</param>
|
||||
/// <param name="otherBeatmaps">The other beatmaps contained within this set.</param>
|
||||
/// <param name="force">Whether to re-query if the provided beatmap already has populated values.</param>
|
||||
/// <returns>True if population was successful.</returns>
|
||||
private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable<BeatmapInfo> otherBeatmaps, bool force = false)
|
||||
private bool fetchAndPopulateOnlineValues(BeatmapInfo beatmap, IEnumerable<BeatmapInfo> otherBeatmaps, bool force = false)
|
||||
{
|
||||
if (api?.State != APIState.Online)
|
||||
return false;
|
||||
|
||||
if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null)
|
||||
if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null
|
||||
&& beatmap.Status != BeatmapSetOnlineStatus.None && beatmap.BeatmapSet.Status != BeatmapSetOnlineStatus.None)
|
||||
return true;
|
||||
|
||||
Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database);
|
||||
Logger.Log("Attempting online lookup for the missing values...", LoggingTarget.Database);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -427,6 +415,9 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database);
|
||||
|
||||
beatmap.Status = res.Status;
|
||||
beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
|
||||
|
||||
if (otherBeatmaps.Any(b => b.OnlineBeatmapID == res.OnlineBeatmapID))
|
||||
{
|
||||
Logger.Log("Another beatmap in the same set already mapped to this ID. We'll skip adding it this time.", LoggingTarget.Database);
|
||||
@@ -435,6 +426,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
|
||||
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -10,7 +10,6 @@ using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Graphics.Textures;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Storyboards;
|
||||
|
||||
@@ -45,6 +44,10 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath;
|
||||
|
||||
private LargeTextureStore textureStore;
|
||||
|
||||
protected override bool BackgroundStillValid(Texture b) => false; // bypass lazy logic. we want to return a new background each time for refcounting purposes.
|
||||
|
||||
protected override Texture GetBackground()
|
||||
{
|
||||
if (Metadata?.BackgroundFile == null)
|
||||
@@ -52,7 +55,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
try
|
||||
{
|
||||
return new LargeTextureStore(new RawTextureLoaderStore(store)).Get(getPathForFile(Metadata.BackgroundFile));
|
||||
return (textureStore ?? (textureStore = new LargeTextureStore(new TextureLoaderStore(store)))).Get(getPathForFile(Metadata.BackgroundFile));
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -73,6 +76,14 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
public override void TransferTo(WorkingBeatmap other)
|
||||
{
|
||||
base.TransferTo(other);
|
||||
|
||||
if (other is BeatmapManagerWorkingBeatmap owb && textureStore != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo))
|
||||
owb.textureStore = textureStore;
|
||||
}
|
||||
|
||||
protected override Waveform GetWaveform()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
@@ -44,6 +45,25 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public virtual void PostProcess()
|
||||
{
|
||||
void updateNestedCombo(HitObject obj, int comboIndex, int indexInCurrentCombo)
|
||||
{
|
||||
if (obj is IHasComboInformation objectComboInfo)
|
||||
{
|
||||
objectComboInfo.ComboIndex = comboIndex;
|
||||
objectComboInfo.IndexInCurrentCombo = indexInCurrentCombo;
|
||||
foreach (var nestedObject in obj.NestedHitObjects)
|
||||
updateNestedCombo(nestedObject, comboIndex, indexInCurrentCombo);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var hitObject in Beatmap.HitObjects)
|
||||
{
|
||||
if (hitObject is IHasComboInformation objectComboInfo)
|
||||
{
|
||||
foreach (var nested in hitObject.NestedHitObjects)
|
||||
updateNestedCombo(nested, objectComboInfo.ComboIndex, objectComboInfo.IndexInCurrentCombo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,12 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public int? OnlineBeatmapSetID
|
||||
{
|
||||
get { return onlineBeatmapSetID; }
|
||||
set { onlineBeatmapSetID = value > 0 ? value : null; }
|
||||
get => onlineBeatmapSetID;
|
||||
set => onlineBeatmapSetID = value > 0 ? value : null;
|
||||
}
|
||||
|
||||
public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
|
||||
|
||||
public BeatmapMetadata Metadata { get; set; }
|
||||
|
||||
public List<BeatmapInfo> Beatmaps { get; set; }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// 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 osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@@ -14,20 +13,35 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
private readonly OsuSpriteText statusText;
|
||||
|
||||
private BeatmapSetOnlineStatus status = BeatmapSetOnlineStatus.None;
|
||||
private BeatmapSetOnlineStatus status;
|
||||
|
||||
public BeatmapSetOnlineStatus Status
|
||||
{
|
||||
get { return status; }
|
||||
get => status;
|
||||
set
|
||||
{
|
||||
if (value == status) return;
|
||||
if (status == value)
|
||||
return;
|
||||
status = value;
|
||||
|
||||
statusText.Text = Enum.GetName(typeof(BeatmapSetOnlineStatus), Status)?.ToUpperInvariant();
|
||||
Alpha = value == BeatmapSetOnlineStatus.None ? 0 : 1;
|
||||
statusText.Text = value.ToString().ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
public BeatmapSetOnlineStatusPill(float textSize, MarginPadding textPadding)
|
||||
public float TextSize
|
||||
{
|
||||
get => statusText.TextSize;
|
||||
set => statusText.TextSize = value;
|
||||
}
|
||||
|
||||
public MarginPadding TextPadding
|
||||
{
|
||||
get => statusText.Padding;
|
||||
set => statusText.Padding = value;
|
||||
}
|
||||
|
||||
public BeatmapSetOnlineStatusPill()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Masking = true;
|
||||
@@ -45,10 +59,10 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Font = @"Exo2.0-Bold",
|
||||
TextSize = textSize,
|
||||
Padding = textPadding,
|
||||
},
|
||||
};
|
||||
|
||||
Status = BeatmapSetOnlineStatus.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,11 +55,11 @@ namespace osu.Game.Beatmaps.Formats
|
||||
} while (line != null && line.Length == 0);
|
||||
|
||||
if (line == null)
|
||||
throw new IOException(@"Unknown file format");
|
||||
throw new IOException(@"Unknown file format (null)");
|
||||
|
||||
var decoder = typedDecoders.Select(d => line.StartsWith(d.Key) ? d.Value : null).FirstOrDefault();
|
||||
var decoder = typedDecoders.Select(d => line.StartsWith(d.Key, StringComparison.InvariantCulture) ? d.Value : null).FirstOrDefault();
|
||||
if (decoder == null)
|
||||
throw new IOException(@"Unknown file format");
|
||||
throw new IOException($@"Unknown file format ({line})");
|
||||
|
||||
return (Decoder<T>)decoder.Invoke(line);
|
||||
}
|
||||
|
||||
@@ -408,11 +408,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
|
||||
|
||||
var obj = parser.Parse(line);
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
beatmap.HitObjects.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0);
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (ShouldSkipLine(line))
|
||||
continue;
|
||||
|
||||
if (line.StartsWith(@"[") && line.EndsWith(@"]"))
|
||||
if (line.StartsWith(@"[", StringComparison.Ordinal) && line.EndsWith(@"]", StringComparison.Ordinal))
|
||||
{
|
||||
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
|
||||
{
|
||||
@@ -53,7 +53,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//");
|
||||
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//", StringComparison.Ordinal);
|
||||
|
||||
protected virtual void ParseLine(T output, Section section, string line)
|
||||
{
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
private void handleEvents(string line)
|
||||
{
|
||||
var depth = 0;
|
||||
while (line.StartsWith(" ") || line.StartsWith("_"))
|
||||
while (line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal))
|
||||
{
|
||||
++depth;
|
||||
line = line.Substring(1);
|
||||
@@ -269,9 +269,9 @@ namespace osu.Game.Beatmaps.Formats
|
||||
return Anchor.BottomCentre;
|
||||
case LegacyOrigins.BottomRight:
|
||||
return Anchor.BottomRight;
|
||||
default:
|
||||
return Anchor.TopLeft;
|
||||
}
|
||||
|
||||
throw new InvalidDataException($@"Unknown origin: {value}");
|
||||
}
|
||||
|
||||
private void handleVariables(string line)
|
||||
|
||||
@@ -8,10 +8,10 @@ using osu.Game.Rulesets.Mods;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Framework.IO.File;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@@ -38,12 +38,26 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
Mods.ValueChanged += mods => applyRateAdjustments();
|
||||
|
||||
beatmap = new AsyncLazy<IBeatmap>(populateBeatmap);
|
||||
background = new AsyncLazy<Texture>(populateBackground, b => b == null || !b.IsDisposed);
|
||||
track = new AsyncLazy<Track>(populateTrack);
|
||||
waveform = new AsyncLazy<Waveform>(populateWaveform);
|
||||
storyboard = new AsyncLazy<Storyboard>(populateStoryboard);
|
||||
skin = new AsyncLazy<Skin>(populateSkin);
|
||||
beatmap = new RecyclableLazy<IBeatmap>(() =>
|
||||
{
|
||||
var b = GetBeatmap() ?? new Beatmap();
|
||||
// use the database-backed info.
|
||||
b.BeatmapInfo = BeatmapInfo;
|
||||
return b;
|
||||
});
|
||||
|
||||
track = new RecyclableLazy<Track>(() =>
|
||||
{
|
||||
// we want to ensure that we always have a track, even if it's a fake one.
|
||||
var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap);
|
||||
applyRateAdjustments(t);
|
||||
return t;
|
||||
});
|
||||
|
||||
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
|
||||
waveform = new RecyclableLazy<Waveform>(GetWaveform);
|
||||
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
|
||||
skin = new RecyclableLazy<Skin>(GetSkin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -58,28 +72,6 @@ namespace osu.Game.Beatmaps
|
||||
return path;
|
||||
}
|
||||
|
||||
protected abstract IBeatmap GetBeatmap();
|
||||
protected abstract Texture GetBackground();
|
||||
protected abstract Track GetTrack();
|
||||
protected virtual Skin GetSkin() => new DefaultSkin();
|
||||
protected virtual Waveform GetWaveform() => new Waveform();
|
||||
protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo };
|
||||
|
||||
public bool BeatmapLoaded => beatmap.IsResultAvailable;
|
||||
public IBeatmap Beatmap => beatmap.Value.Result;
|
||||
public async Task<IBeatmap> GetBeatmapAsync() => await beatmap.Value;
|
||||
private readonly AsyncLazy<IBeatmap> beatmap;
|
||||
|
||||
private IBeatmap populateBeatmap()
|
||||
{
|
||||
var b = GetBeatmap() ?? new Beatmap();
|
||||
|
||||
// use the database-backed info.
|
||||
b.BeatmapInfo = BeatmapInfo;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>.
|
||||
/// <para>
|
||||
@@ -136,62 +128,53 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public override string ToString() => BeatmapInfo.ToString();
|
||||
|
||||
public bool BackgroundLoaded => background.IsResultAvailable;
|
||||
public Texture Background => background.Value.Result;
|
||||
public async Task<Texture> GetBackgroundAsync() => await background.Value;
|
||||
private AsyncLazy<Texture> background;
|
||||
public bool BeatmapLoaded => beatmap.IsResultAvailable;
|
||||
public IBeatmap Beatmap => beatmap.Value;
|
||||
protected abstract IBeatmap GetBeatmap();
|
||||
private readonly RecyclableLazy<IBeatmap> beatmap;
|
||||
|
||||
private Texture populateBackground() => GetBackground();
|
||||
public bool BackgroundLoaded => background.IsResultAvailable;
|
||||
public Texture Background => background.Value;
|
||||
protected virtual bool BackgroundStillValid(Texture b) => b == null || b.Available;
|
||||
protected abstract Texture GetBackground();
|
||||
private readonly RecyclableLazy<Texture> background;
|
||||
|
||||
public bool TrackLoaded => track.IsResultAvailable;
|
||||
public Track Track => track.Value.Result;
|
||||
public async Task<Track> GetTrackAsync() => await track.Value;
|
||||
private AsyncLazy<Track> track;
|
||||
|
||||
private Track populateTrack()
|
||||
{
|
||||
// we want to ensure that we always have a track, even if it's a fake one.
|
||||
var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap);
|
||||
applyRateAdjustments(t);
|
||||
return t;
|
||||
}
|
||||
public Track Track => track.Value;
|
||||
protected abstract Track GetTrack();
|
||||
private RecyclableLazy<Track> track;
|
||||
|
||||
public bool WaveformLoaded => waveform.IsResultAvailable;
|
||||
public Waveform Waveform => waveform.Value.Result;
|
||||
public async Task<Waveform> GetWaveformAsync() => await waveform.Value;
|
||||
private readonly AsyncLazy<Waveform> waveform;
|
||||
|
||||
private Waveform populateWaveform() => GetWaveform();
|
||||
public Waveform Waveform => waveform.Value;
|
||||
protected virtual Waveform GetWaveform() => new Waveform();
|
||||
private readonly RecyclableLazy<Waveform> waveform;
|
||||
|
||||
public bool StoryboardLoaded => storyboard.IsResultAvailable;
|
||||
public Storyboard Storyboard => storyboard.Value.Result;
|
||||
public async Task<Storyboard> GetStoryboardAsync() => await storyboard.Value;
|
||||
private readonly AsyncLazy<Storyboard> storyboard;
|
||||
|
||||
private Storyboard populateStoryboard() => GetStoryboard();
|
||||
public Storyboard Storyboard => storyboard.Value;
|
||||
protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo };
|
||||
private readonly RecyclableLazy<Storyboard> storyboard;
|
||||
|
||||
public bool SkinLoaded => skin.IsResultAvailable;
|
||||
public Skin Skin => skin.Value.Result;
|
||||
public async Task<Skin> GetSkinAsync() => await skin.Value;
|
||||
private readonly AsyncLazy<Skin> skin;
|
||||
public Skin Skin => skin.Value;
|
||||
protected virtual Skin GetSkin() => new DefaultSkin();
|
||||
private readonly RecyclableLazy<Skin> skin;
|
||||
|
||||
private Skin populateSkin() => GetSkin();
|
||||
|
||||
public void TransferTo(WorkingBeatmap other)
|
||||
/// <summary>
|
||||
/// Transfer pieces of a beatmap to a new one, where possible, to save on loading.
|
||||
/// </summary>
|
||||
/// <param name="other">The new beatmap which is being switched to.</param>
|
||||
public virtual void TransferTo(WorkingBeatmap other)
|
||||
{
|
||||
if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
|
||||
other.track = track;
|
||||
|
||||
if (background.IsResultAvailable && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo))
|
||||
other.background = background;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (BackgroundLoaded) Background?.Dispose();
|
||||
if (WaveformLoaded) Waveform?.Dispose();
|
||||
if (StoryboardLoaded) Storyboard?.Dispose();
|
||||
if (SkinLoaded) Skin?.Dispose();
|
||||
background.Recycle();
|
||||
waveform.Recycle();
|
||||
storyboard.Recycle();
|
||||
skin.Recycle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -210,15 +193,15 @@ namespace osu.Game.Beatmaps
|
||||
mod.ApplyToClock(t);
|
||||
}
|
||||
|
||||
public class AsyncLazy<T>
|
||||
public class RecyclableLazy<T>
|
||||
{
|
||||
private Lazy<Task<T>> lazy;
|
||||
private Lazy<T> lazy;
|
||||
private readonly Func<T> valueFactory;
|
||||
private readonly Func<T, bool> stillValidFunction;
|
||||
|
||||
private readonly object initLock = new object();
|
||||
private readonly object fetchLock = new object();
|
||||
|
||||
public AsyncLazy(Func<T> valueFactory, Func<T, bool> stillValidFunction = null)
|
||||
public RecyclableLazy(Func<T> valueFactory, Func<T, bool> stillValidFunction = null)
|
||||
{
|
||||
this.valueFactory = valueFactory;
|
||||
this.stillValidFunction = stillValidFunction;
|
||||
@@ -230,45 +213,28 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
if (!IsResultAvailable) return;
|
||||
|
||||
(lazy.Value.Result as IDisposable)?.Dispose();
|
||||
(lazy.Value as IDisposable)?.Dispose();
|
||||
recreate();
|
||||
}
|
||||
|
||||
public bool IsResultAvailable
|
||||
public bool IsResultAvailable => stillValid;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
recreateIfInvalid();
|
||||
return lazy.Value.IsCompleted;
|
||||
lock (fetchLock)
|
||||
{
|
||||
if (!stillValid)
|
||||
recreate();
|
||||
return lazy.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task<T> Value
|
||||
{
|
||||
get
|
||||
{
|
||||
recreateIfInvalid();
|
||||
return lazy.Value;
|
||||
}
|
||||
}
|
||||
private bool stillValid => lazy.IsValueCreated && (stillValidFunction?.Invoke(lazy.Value) ?? true);
|
||||
|
||||
private void recreateIfInvalid()
|
||||
{
|
||||
lock (initLock)
|
||||
{
|
||||
if (!lazy.IsValueCreated || !lazy.Value.IsCompleted)
|
||||
// we have not yet been initialised or haven't run the task.
|
||||
return;
|
||||
|
||||
if (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)
|
||||
// we are still in a valid state.
|
||||
return;
|
||||
|
||||
recreate();
|
||||
}
|
||||
}
|
||||
|
||||
private void recreate() => lazy = new Lazy<Task<T>>(() => Task.Run(valueFactory));
|
||||
private void recreate() => lazy = new Lazy<T>(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,8 +83,6 @@ namespace osu.Game.Configuration
|
||||
|
||||
Set(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised);
|
||||
|
||||
Set(OsuSetting.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential);
|
||||
|
||||
Set(OsuSetting.IncreaseFirstObjectVisibility, true);
|
||||
|
||||
// Update
|
||||
@@ -143,7 +141,6 @@ namespace osu.Game.Configuration
|
||||
ChatDisplayHeight,
|
||||
Version,
|
||||
ShowConvertedBeatmaps,
|
||||
SpeedChangeVisualisation,
|
||||
Skin,
|
||||
ScreenshotFormat,
|
||||
ScreenshotCaptureMenuCursor,
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace osu.Game.Configuration
|
||||
[Description("Sequential")]
|
||||
Sequential,
|
||||
[Description("Overlapping")]
|
||||
Overlapping
|
||||
Overlapping,
|
||||
[Description("Constant")]
|
||||
Constant
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.IO.File;
|
||||
@@ -58,7 +59,7 @@ namespace osu.Game.Database
|
||||
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
|
||||
private ArchiveImportIPCChannel ipc;
|
||||
|
||||
private readonly List<Action> cachedEvents = new List<Action>();
|
||||
private readonly List<Action> queuedEvents = new List<Action>();
|
||||
|
||||
/// <summary>
|
||||
/// Allows delaying of outwards events until an operation is confirmed (at a database level).
|
||||
@@ -76,20 +77,27 @@ namespace osu.Game.Database
|
||||
/// <param name="perform">Whether the flushed events should be performed.</param>
|
||||
private void flushEvents(bool perform)
|
||||
{
|
||||
Action[] events;
|
||||
lock (queuedEvents)
|
||||
{
|
||||
events = queuedEvents.ToArray();
|
||||
queuedEvents.Clear();
|
||||
}
|
||||
|
||||
if (perform)
|
||||
{
|
||||
foreach (var a in cachedEvents)
|
||||
foreach (var a in events)
|
||||
a.Invoke();
|
||||
}
|
||||
|
||||
cachedEvents.Clear();
|
||||
delayingEvents = false;
|
||||
}
|
||||
|
||||
private void handleEvent(Action a)
|
||||
{
|
||||
if (delayingEvents)
|
||||
cachedEvents.Add(a);
|
||||
lock (queuedEvents)
|
||||
queuedEvents.Add(a);
|
||||
else
|
||||
a.Invoke();
|
||||
}
|
||||
@@ -129,7 +137,6 @@ namespace osu.Game.Database
|
||||
List<TModel> imported = new List<TModel>();
|
||||
|
||||
int current = 0;
|
||||
int errors = 0;
|
||||
foreach (string path in paths)
|
||||
{
|
||||
if (notification.State == ProgressNotificationState.Cancelled)
|
||||
@@ -162,12 +169,29 @@ namespace osu.Game.Database
|
||||
{
|
||||
e = e.InnerException ?? e;
|
||||
Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})");
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!";
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
if (imported.Count == 0)
|
||||
{
|
||||
notification.Text = "Import failed!";
|
||||
notification.State = ProgressNotificationState.Cancelled;
|
||||
}
|
||||
else
|
||||
{
|
||||
notification.CompletionText = $"Imported {current} {typeof(TModel).Name.Replace("Info", "").ToLower()}s!";
|
||||
notification.CompletionClickAction += () =>
|
||||
{
|
||||
if (imported.Count > 0)
|
||||
PresentCompletedImport(imported);
|
||||
return true;
|
||||
};
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void PresentCompletedImport(IEnumerable<TModel> imported)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -178,7 +202,8 @@ namespace osu.Game.Database
|
||||
{
|
||||
try
|
||||
{
|
||||
return Import(CreateModel(archive), archive);
|
||||
var model = CreateModel(archive);
|
||||
return model == null ? null : Import(model, archive);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -257,17 +282,19 @@ namespace osu.Game.Database
|
||||
/// Is a no-op for already deleted items.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to delete.</param>
|
||||
public void Delete(TModel item)
|
||||
/// <returns>false if no operation was performed</returns>
|
||||
public bool Delete(TModel item)
|
||||
{
|
||||
using (ContextFactory.GetForWrite())
|
||||
{
|
||||
// re-fetch the model on the import context.
|
||||
var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == item.ID);
|
||||
var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).FirstOrDefault(s => s.ID == item.ID);
|
||||
|
||||
if (foundModel.DeletePending) return;
|
||||
if (foundModel == null || foundModel.DeletePending) return false;
|
||||
|
||||
if (ModelStore.Delete(foundModel))
|
||||
Files.Dereference(foundModel.Files.Select(f => f.FileInfo).ToArray());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +309,7 @@ namespace osu.Game.Database
|
||||
var notification = new ProgressNotification
|
||||
{
|
||||
Progress = 0,
|
||||
CompletionText = "Deleted all beatmaps!",
|
||||
CompletionText = $"Deleted all {typeof(TModel).Name.Replace("Info", "").ToLower()}s!",
|
||||
State = ProgressNotificationState.Active,
|
||||
};
|
||||
|
||||
@@ -384,12 +411,54 @@ namespace osu.Game.Database
|
||||
return fileInfos;
|
||||
}
|
||||
|
||||
#region osu-stable import
|
||||
|
||||
/// <summary>
|
||||
/// Set a storage with access to an osu-stable install for import purposes.
|
||||
/// </summary>
|
||||
public Func<Storage> GetStableStorage { private get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Denotes whether an osu-stable installation is present to perform automated imports from.
|
||||
/// </summary>
|
||||
public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null;
|
||||
|
||||
/// <summary>
|
||||
/// The relative path from osu-stable's data directory to import items from.
|
||||
/// </summary>
|
||||
protected virtual string ImportFromStablePath => null;
|
||||
|
||||
/// <summary>
|
||||
/// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future.
|
||||
/// </summary>
|
||||
public Task ImportFromStableAsync()
|
||||
{
|
||||
var stable = GetStableStorage?.Invoke();
|
||||
|
||||
if (stable == null)
|
||||
{
|
||||
Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (!stable.ExistsDirectory(ImportFromStablePath))
|
||||
{
|
||||
// This handles situations like when the user does not have a Skins folder
|
||||
Logger.Log($"No {ImportFromStablePath} folder available in osu!stable installation", LoggingTarget.Information, LogLevel.Error);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Create a barebones model from the provided archive.
|
||||
/// Actual expensive population should be done in <see cref="Populate"/>; this should just prepare for duplicate checking.
|
||||
/// </summary>
|
||||
/// <param name="archive">The archive to create the model for.</param>
|
||||
/// <returns>A model populated with minimal information.</returns>
|
||||
/// <returns>A model populated with minimal information. Returning a null will abort importing silently.</returns>
|
||||
protected abstract TModel CreateModel(ArchiveReader archive);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -5,7 +5,6 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace osu.Game.Database
|
||||
@@ -118,7 +117,9 @@ namespace osu.Game.Database
|
||||
|
||||
private void recycleThreadContexts()
|
||||
{
|
||||
threadContexts?.Values.ForEach(c => c.Dispose());
|
||||
// Contexts for other threads are not disposed as they may be in use elsewhere. Instead, fresh contexts are exposed
|
||||
// for other threads to use, and we rely on the finalizer inside OsuDbContext to handle their previous contexts
|
||||
threadContexts?.Value.Dispose();
|
||||
threadContexts = new ThreadLocal<OsuDbContext>(CreateContext, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,13 @@ namespace osu.Game.Database
|
||||
}
|
||||
}
|
||||
|
||||
~OsuDbContext()
|
||||
{
|
||||
// DbContext does not contain a finalizer (https://github.com/aspnet/EntityFrameworkCore/issues/8872)
|
||||
// This is used to clean up previous contexts when fresh contexts are exposed via DatabaseContextFactory
|
||||
Dispose();
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
|
||||
@@ -5,8 +5,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Graphics.Textures;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
@@ -28,7 +27,6 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = Color4.DarkGray,
|
||||
FillMode = FillMode.Fill,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,10 +30,6 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
/// </summary>
|
||||
private const float edge_smoothness = 1;
|
||||
|
||||
public override bool HandleKeyboardInput => false;
|
||||
public override bool HandleMouseInput => false;
|
||||
|
||||
|
||||
public Color4 ColourLight = Color4.White;
|
||||
public Color4 ColourDark = Color4.Black;
|
||||
|
||||
@@ -116,7 +112,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
|
||||
float adjustedAlpha = HideAlphaDiscrepancies ?
|
||||
// Cubically scale alpha to make it drop off more sharply.
|
||||
(float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) :
|
||||
(float)Math.Pow(DrawColourInfo.Colour.AverageColour.Linear.A, 3) :
|
||||
1;
|
||||
|
||||
float elapsedSeconds = (float)Time.Elapsed / 1000;
|
||||
@@ -235,7 +231,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
Vector2Extensions.Transform(particle.Position * Size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix)
|
||||
);
|
||||
|
||||
ColourInfo colourInfo = DrawInfo.Colour;
|
||||
ColourInfo colourInfo = DrawColourInfo.Colour;
|
||||
colourInfo.ApplyChild(particle.Colour);
|
||||
|
||||
Texture.DrawTriangle(
|
||||
|
||||
@@ -20,8 +20,6 @@ namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput => true;
|
||||
|
||||
private OsuGame game;
|
||||
|
||||
private Action showNotImplementedError;
|
||||
|
||||
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays;
|
||||
@@ -22,7 +22,7 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
protected virtual bool PlaySamplesOnStateChange => true;
|
||||
|
||||
protected override bool BlockPassThroughKeyboard => true;
|
||||
protected override bool BlockNonPositionalInput => true;
|
||||
|
||||
private PreviewTrackManager previewTrackManager;
|
||||
|
||||
@@ -54,20 +54,20 @@ namespace osu.Game.Graphics.Containers
|
||||
/// Whether mouse input should be blocked screen-wide while this overlay is visible.
|
||||
/// Performing mouse actions outside of the valid extents will hide the overlay.
|
||||
/// </summary>
|
||||
public virtual bool BlockScreenWideMouse => BlockPassThroughMouse;
|
||||
public virtual bool BlockScreenWideMouse => BlockPositionalInput;
|
||||
|
||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos);
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
|
||||
if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
|
||||
{
|
||||
State = Visibility.Hidden;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnClick(state);
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
public virtual bool OnPressed(GlobalAction action)
|
||||
|
||||
@@ -6,7 +6,7 @@ using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
@@ -18,16 +18,16 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
protected virtual IEnumerable<Drawable> EffectTargets => new[] { Content };
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
EffectTargets.ForEach(d => d.FadeColour(HoverColour, 500, Easing.OutQuint));
|
||||
return base.OnHover(state);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
EffectTargets.ForEach(d => d.FadeColour(IdleColour, 500, Easing.OutQuint));
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.EventArgs;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
@@ -21,7 +20,7 @@ namespace osu.Game.Graphics.Containers
|
||||
/// </summary>
|
||||
public double DistanceDecayOnRightMouseScrollbar = 0.02;
|
||||
|
||||
private bool shouldPerformRightMouseScroll(InputState state) => RightMouseScrollbar && state.Mouse.IsPressed(MouseButton.Right);
|
||||
private bool shouldPerformRightMouseScroll(MouseButtonEvent e) => RightMouseScrollbar && e.Button == MouseButton.Right;
|
||||
|
||||
private void scrollToRelative(float value) => ScrollTo(Clamp((value - Scrollbar.DrawSize[ScrollDim] / 2) / Scrollbar.Size[ScrollDim]), true, DistanceDecayOnRightMouseScrollbar);
|
||||
|
||||
@@ -29,40 +28,40 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging;
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
if (shouldPerformRightMouseScroll(state))
|
||||
if (shouldPerformRightMouseScroll(e))
|
||||
{
|
||||
scrollToRelative(state.Mouse.Position[ScrollDim]);
|
||||
scrollToRelative(e.MousePosition[ScrollDim]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseDown(state, args);
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
protected override bool OnDrag(InputState state)
|
||||
protected override bool OnDrag(DragEvent e)
|
||||
{
|
||||
if (mouseScrollBarDragging)
|
||||
{
|
||||
scrollToRelative(state.Mouse.Position[ScrollDim]);
|
||||
scrollToRelative(e.MousePosition[ScrollDim]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnDrag(state);
|
||||
return base.OnDrag(e);
|
||||
}
|
||||
|
||||
protected override bool OnDragStart(InputState state)
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
if (shouldPerformRightMouseScroll(state))
|
||||
if (shouldPerformRightMouseScroll(e))
|
||||
{
|
||||
mouseScrollBarDragging = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnDragStart(state);
|
||||
return base.OnDragStart(e);
|
||||
}
|
||||
|
||||
protected override bool OnDragEnd(InputState state)
|
||||
protected override bool OnDragEnd(DragEndEvent e)
|
||||
{
|
||||
if (mouseScrollBarDragging)
|
||||
{
|
||||
@@ -70,7 +69,7 @@ namespace osu.Game.Graphics.Containers
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnDragEnd(state);
|
||||
return base.OnDragEnd(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
if (parallaxEnabled)
|
||||
{
|
||||
Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount;
|
||||
Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.Position) - DrawSize / 2) * ParallaxAmount;
|
||||
|
||||
double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000);
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// A container that adds the ability to shake its contents.
|
||||
/// </summary>
|
||||
public class ShakeContainer : Container
|
||||
{
|
||||
/// <summary>
|
||||
/// Shake the contents of this container.
|
||||
/// </summary>
|
||||
/// <param name="maximumLength">The maximum length the shake should last.</param>
|
||||
public void Shake(double maximumLength)
|
||||
{
|
||||
const float shake_amount = 8;
|
||||
const float shake_duration = 30;
|
||||
|
||||
// if we don't have enough time, don't bother shaking.
|
||||
if (maximumLength < shake_duration * 2)
|
||||
return;
|
||||
|
||||
var sequence = this.MoveToX(shake_amount, shake_duration / 2, Easing.OutSine).Then()
|
||||
.MoveToX(-shake_amount, shake_duration, Easing.InOutSine).Then();
|
||||
|
||||
// if we don't have enough time for the second shake, skip it.
|
||||
if (maximumLength > shake_duration * 4)
|
||||
sequence = sequence
|
||||
.MoveToX(shake_amount, shake_duration, Easing.InOutSine).Then()
|
||||
.MoveToX(-shake_amount, shake_duration, Easing.InOutSine).Then();
|
||||
|
||||
sequence.MoveToX(0, shake_duration / 2, Easing.InSine);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,7 @@ using osu.Game.Configuration;
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.EventArgs;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Graphics.Cursor
|
||||
@@ -40,11 +39,11 @@ namespace osu.Game.Graphics.Cursor
|
||||
screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility);
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
if (dragRotationState != DragRotationState.NotDragging)
|
||||
{
|
||||
var position = state.Mouse.Position;
|
||||
var position = e.MousePosition;
|
||||
var distance = Vector2Extensions.Distance(position, positionMouseDown);
|
||||
// don't start rotating until we're moved a minimum distance away from the mouse down location,
|
||||
// else it can have an annoying effect.
|
||||
@@ -53,7 +52,7 @@ namespace osu.Game.Graphics.Cursor
|
||||
// don't rotate when distance is zero to avoid NaN
|
||||
if (dragRotationState == DragRotationState.Rotating && distance > 0)
|
||||
{
|
||||
Vector2 offset = state.Mouse.Position - positionMouseDown;
|
||||
Vector2 offset = e.MousePosition - positionMouseDown;
|
||||
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
|
||||
|
||||
// Always rotate in the direction of least distance
|
||||
@@ -66,13 +65,13 @@ namespace osu.Game.Graphics.Cursor
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnMouseMove(state);
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
// only trigger animation for main mouse buttons
|
||||
if (args.Button <= MouseButton.Right)
|
||||
if (e.Button <= MouseButton.Right)
|
||||
{
|
||||
activeCursor.Scale = new Vector2(1);
|
||||
activeCursor.ScaleTo(0.90f, 800, Easing.OutQuint);
|
||||
@@ -81,29 +80,29 @@ namespace osu.Game.Graphics.Cursor
|
||||
activeCursor.AdditiveLayer.FadeInFromZero(800, Easing.OutQuint);
|
||||
}
|
||||
|
||||
if (args.Button == MouseButton.Left && cursorRotate)
|
||||
if (e.Button == MouseButton.Left && cursorRotate)
|
||||
{
|
||||
dragRotationState = DragRotationState.DragStarted;
|
||||
positionMouseDown = state.Mouse.Position;
|
||||
positionMouseDown = e.MousePosition;
|
||||
}
|
||||
return base.OnMouseDown(state, args);
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
protected override bool OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
if (!state.Mouse.HasMainButtonPressed)
|
||||
if (!e.IsPressed(MouseButton.Left) && !e.IsPressed(MouseButton.Right))
|
||||
{
|
||||
activeCursor.AdditiveLayer.FadeOutFromOne(500, Easing.OutQuint);
|
||||
activeCursor.ScaleTo(1, 500, Easing.OutElastic);
|
||||
}
|
||||
|
||||
if (args.Button == MouseButton.Left)
|
||||
if (e.Button == MouseButton.Left)
|
||||
{
|
||||
if (dragRotationState == DragRotationState.Rotating)
|
||||
activeCursor.RotateTo(0, 600 * (1 + Math.Abs(activeCursor.Rotation / 720)), Easing.OutElasticHalf);
|
||||
dragRotationState = DragRotationState.NotDragging;
|
||||
}
|
||||
return base.OnMouseUp(state, args);
|
||||
return base.OnMouseUp(e);
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using System;
|
||||
using Humanizer;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
@@ -16,7 +15,6 @@ namespace osu.Game.Graphics
|
||||
|
||||
public DrawableDate(DateTimeOffset date)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Font = "Exo2.0-RegularItalic";
|
||||
|
||||
Date = date.ToLocalTime();
|
||||
@@ -56,8 +54,6 @@ namespace osu.Game.Graphics
|
||||
Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate);
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput => true;
|
||||
|
||||
protected virtual string Format() => Date.Humanize();
|
||||
|
||||
private void updateTime() => Text = Format();
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace osu.Game.Graphics
|
||||
|
||||
private volatile int screenShotTasks;
|
||||
|
||||
public async Task TakeScreenshotAsync() => await Task.Run(async () =>
|
||||
public Task TakeScreenshotAsync() => Task.Run(async () =>
|
||||
{
|
||||
Interlocked.Increment(ref screenShotTasks);
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace osu.Game.Graphics
|
||||
|
||||
if (loadableIcon == loadedIcon) return;
|
||||
|
||||
var texture = store?.Get(((char)loadableIcon).ToString());
|
||||
var texture = store.Get(((char)loadableIcon).ToString());
|
||||
|
||||
spriteMain.Texture = texture;
|
||||
spriteShadow.Texture = texture;
|
||||
@@ -95,7 +95,7 @@ namespace osu.Game.Graphics
|
||||
{
|
||||
//adjust shadow alpha based on highest component intensity to avoid muddy display of darker text.
|
||||
//squared result for quadratic fall-off seems to give the best result.
|
||||
var avgColour = (Color4)DrawInfo.Colour.AverageColour;
|
||||
var avgColour = (Color4)DrawColourInfo.Colour.AverageColour;
|
||||
|
||||
spriteShadow.Alpha = (float)Math.Pow(Math.Max(Math.Max(avgColour.R, avgColour.G), avgColour.B), 2);
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace osu.Game.Graphics
|
||||
if (icon == value) return;
|
||||
|
||||
icon = value;
|
||||
if (IsLoaded)
|
||||
if (LoadState == LoadState.Loaded)
|
||||
updateTexture();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.MathUtils;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
|
||||
namespace osu.Game.Graphics.Sprites
|
||||
@@ -19,27 +16,6 @@ namespace osu.Game.Graphics.Sprites
|
||||
Shadow = true;
|
||||
TextSize = FONT_SIZE;
|
||||
}
|
||||
|
||||
protected override Drawable CreateFallbackCharacterDrawable()
|
||||
{
|
||||
var tex = GetTextureForCharacter('?');
|
||||
|
||||
if (tex != null)
|
||||
{
|
||||
float adjust = (RNG.NextSingle() - 0.5f) * 2;
|
||||
return new Sprite
|
||||
{
|
||||
Texture = tex,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Scale = new Vector2(1 + adjust * 0.2f),
|
||||
Rotation = adjust * 15,
|
||||
Colour = Color4.White,
|
||||
};
|
||||
}
|
||||
|
||||
return base.CreateFallbackCharacterDrawable();
|
||||
}
|
||||
}
|
||||
|
||||
public static class OsuSpriteTextTransformExtensions
|
||||
|
||||
@@ -1,18 +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.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
|
||||
namespace osu.Game.Graphics.Textures
|
||||
{
|
||||
/// <summary>
|
||||
/// A texture store that bypasses atlasing.
|
||||
/// </summary>
|
||||
public class LargeTextureStore : TextureStore
|
||||
{
|
||||
public LargeTextureStore(IResourceStore<RawTexture> store = null) : base(store, false)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,10 +47,10 @@ namespace osu.Game.Graphics.UserInterface
|
||||
public readonly SpriteIcon Chevron;
|
||||
|
||||
//don't allow clicking between transitions and don't make the chevron clickable
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceiveMouseInputAt(screenSpacePos);
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
public override bool HandleKeyboardInput => State == Visibility.Visible;
|
||||
public override bool HandleMouseInput => State == Visibility.Visible;
|
||||
public override bool HandleNonPositionalInput => State == Visibility.Visible;
|
||||
public override bool HandlePositionalInput => State == Visibility.Visible;
|
||||
public override bool IsRemovable => true;
|
||||
|
||||
private Visibility state;
|
||||
|
||||
@@ -13,7 +13,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@@ -211,9 +211,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos);
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In);
|
||||
flash();
|
||||
@@ -225,20 +225,20 @@ namespace osu.Game.Graphics.UserInterface
|
||||
glowContainer.FadeOut();
|
||||
});
|
||||
|
||||
return base.OnClick(state);
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(state);
|
||||
base.OnHover(e);
|
||||
|
||||
Selected.Value = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
Selected.Value = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Platform;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
@@ -37,19 +37,19 @@ namespace osu.Game.Graphics.UserInterface
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
InternalChild.FadeColour(hoverColour, 500, Easing.OutQuint);
|
||||
return base.OnHover(state);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
InternalChild.FadeColour(Color4.White, 500, Easing.OutQuint);
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
if(Link != null)
|
||||
host.OpenUrlExternally(Link);
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using System;
|
||||
using osu.Framework.Input.EventArgs;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Input.Bindings;
|
||||
using OpenTK.Input;
|
||||
|
||||
@@ -34,22 +33,22 @@ namespace osu.Game.Graphics.UserInterface
|
||||
}
|
||||
|
||||
// We may not be focused yet, but we need to handle keyboard input to be able to request focus
|
||||
public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput;
|
||||
public override bool HandleNonPositionalInput => HoldFocus || base.HandleNonPositionalInput;
|
||||
|
||||
protected override void OnFocus(InputState state)
|
||||
protected override void OnFocus(FocusEvent e)
|
||||
{
|
||||
base.OnFocus(state);
|
||||
base.OnFocus(e);
|
||||
BorderThickness = 0;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (!HasFocus) return false;
|
||||
|
||||
if (args.Key == Key.Escape)
|
||||
if (e.Key == Key.Escape)
|
||||
return false; // disable the framework-level handling of escape key for confority (we use GlobalAction.Back).
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
public override bool OnPressed(GlobalAction action)
|
||||
|
||||
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@@ -21,10 +21,10 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
sampleClick?.Play();
|
||||
return base.OnClick(state);
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
||||
@@ -8,7 +8,7 @@ using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@@ -28,10 +28,10 @@ namespace osu.Game.Graphics.UserInterface
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
sampleHover?.Play();
|
||||
return base.OnHover(state);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@@ -84,16 +84,16 @@ namespace osu.Game.Graphics.UserInterface
|
||||
});
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
icon.FadeColour(IconHoverColour, 500, Easing.OutQuint);
|
||||
return base.OnHover(state);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
icon.FadeColour(IconColour, 500, Easing.OutQuint);
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.EventArgs;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
@@ -77,34 +76,34 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Enabled.BindValueChanged(enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint), true);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
hover.FadeIn(500, Easing.OutQuint);
|
||||
return base.OnHover(state);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
hover.FadeOut(500, Easing.OutQuint);
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
hover.FlashColour(FlashColour, 800, Easing.OutQuint);
|
||||
return base.OnClick(state);
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
Content.ScaleTo(0.75f, 2000, Easing.OutQuint);
|
||||
return base.OnMouseDown(state, args);
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
protected override bool OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
Content.ScaleTo(1, 1000, Easing.OutElastic);
|
||||
return base.OnMouseUp(state, args);
|
||||
return base.OnMouseUp(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +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.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@@ -10,9 +18,73 @@ namespace osu.Game.Graphics.UserInterface
|
||||
/// </summary>
|
||||
public class OsuButton : Button
|
||||
{
|
||||
private Box hover;
|
||||
|
||||
public OsuButton()
|
||||
{
|
||||
Add(new HoverClickSounds(HoverSampleSet.Loud));
|
||||
Height = 40;
|
||||
|
||||
Content.Masking = true;
|
||||
Content.CornerRadius = 5;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
BackgroundColour = colours.BlueDark;
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
hover = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingMode.Additive,
|
||||
Colour = Color4.White.Opacity(0.1f),
|
||||
Alpha = 0,
|
||||
Depth = -1
|
||||
},
|
||||
new HoverClickSounds(HoverSampleSet.Loud),
|
||||
});
|
||||
|
||||
Enabled.ValueChanged += enabled_ValueChanged;
|
||||
Enabled.TriggerChange();
|
||||
}
|
||||
|
||||
private void enabled_ValueChanged(bool enabled)
|
||||
{
|
||||
this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
hover.FadeIn(200);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
hover.FadeOut(200);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
Content.ScaleTo(0.9f, 4000, Easing.OutQuint);
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
Content.ScaleTo(1, 1000, Easing.OutElastic);
|
||||
return base.OnMouseUp(e);
|
||||
}
|
||||
|
||||
protected override SpriteText CreateText() => new OsuSpriteText
|
||||
{
|
||||
Depth = -1,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Font = @"Exo2.0-Bold",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
@@ -95,18 +95,18 @@ namespace osu.Game.Graphics.UserInterface
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
Nub.Glowing = true;
|
||||
Nub.Expanded = true;
|
||||
return base.OnHover(state);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
Nub.Glowing = false;
|
||||
Nub.Expanded = false;
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
||||
@@ -97,6 +97,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
#region DrawableOsuDropdownMenuItem
|
||||
public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
|
||||
{
|
||||
// IsHovered is used
|
||||
public override bool HandlePositionalInput => true;
|
||||
|
||||
private Color4? accentColour;
|
||||
public Color4 AccentColour
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
|
||||
@@ -97,25 +97,25 @@ namespace osu.Game.Graphics.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
sampleHover.Play();
|
||||
text.BoldText.FadeIn(transition_length, Easing.OutQuint);
|
||||
text.NormalText.FadeOut(transition_length, Easing.OutQuint);
|
||||
return base.OnHover(state);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
text.BoldText.FadeOut(transition_length, Easing.OutQuint);
|
||||
text.NormalText.FadeIn(transition_length, Easing.OutQuint);
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
sampleClick.Play();
|
||||
return base.OnClick(state);
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
protected sealed override Drawable CreateContent() => text = CreateTextContainer();
|
||||
|
||||
@@ -9,8 +9,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.EventArgs;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
@@ -43,23 +42,23 @@ namespace osu.Game.Graphics.UserInterface
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (args.Key == Key.CapsLock)
|
||||
if (e.Key == Key.CapsLock)
|
||||
updateCapsWarning(host.CapsLockEnabled);
|
||||
return base.OnKeyDown(state, args);
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
protected override void OnFocus(InputState state)
|
||||
protected override void OnFocus(FocusEvent e)
|
||||
{
|
||||
updateCapsWarning(host.CapsLockEnabled);
|
||||
base.OnFocus(state);
|
||||
base.OnFocus(e);
|
||||
}
|
||||
|
||||
protected override void OnFocusLost(InputState state)
|
||||
protected override void OnFocusLost(FocusLostEvent e)
|
||||
{
|
||||
updateCapsWarning(false);
|
||||
base.OnFocusLost(state);
|
||||
base.OnFocusLost(e);
|
||||
}
|
||||
|
||||
private void updateCapsWarning(bool visible) => warning.FadeTo(visible ? 1 : 0, 250, Easing.OutQuint);
|
||||
|
||||
@@ -13,8 +13,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.EventArgs;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@@ -125,16 +124,16 @@ namespace osu.Game.Graphics.UserInterface
|
||||
AccentColour = colours.Pink;
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
Nub.Glowing = true;
|
||||
return base.OnHover(state);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
Nub.Glowing = false;
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
protected override void OnUserChange()
|
||||
@@ -164,16 +163,16 @@ namespace osu.Game.Graphics.UserInterface
|
||||
sample.Play();
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
Nub.Current.Value = true;
|
||||
return base.OnMouseDown(state, args);
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
protected override bool OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
Nub.Current.Value = false;
|
||||
return base.OnMouseUp(state, args);
|
||||
return base.OnMouseUp(e);
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
|
||||
@@ -14,7 +14,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
@@ -126,14 +126,14 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Text.FadeColour(AccentColour, transition_length, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
if (!Active)
|
||||
fadeActive();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
if (!Active)
|
||||
fadeInactive();
|
||||
@@ -265,16 +265,16 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Padding = new MarginPadding { Left = 5, Right = 5 };
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
Foreground.Colour = BackgroundColour;
|
||||
return base.OnHover(state);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
Foreground.Colour = BackgroundColourHover;
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@@ -59,18 +59,18 @@ namespace osu.Game.Graphics.UserInterface
|
||||
text.FadeColour(AccentColour, transition_length, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
fadeIn();
|
||||
return base.OnHover(state);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
if (!Current)
|
||||
fadeOut();
|
||||
|
||||
base.OnHoverLost(state);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
||||
@@ -9,7 +9,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Input.Bindings;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
@@ -44,17 +44,17 @@ namespace osu.Game.Graphics.UserInterface
|
||||
BorderColour = colour.Yellow;
|
||||
}
|
||||
|
||||
protected override void OnFocus(InputState state)
|
||||
protected override void OnFocus(FocusEvent e)
|
||||
{
|
||||
BorderThickness = 3;
|
||||
base.OnFocus(state);
|
||||
base.OnFocus(e);
|
||||
}
|
||||
|
||||
protected override void OnFocusLost(InputState state)
|
||||
protected override void OnFocusLost(FocusLostEvent e)
|
||||
{
|
||||
BorderThickness = 0;
|
||||
|
||||
base.OnFocusLost(state);
|
||||
base.OnFocusLost(e);
|
||||
}
|
||||
|
||||
protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), TextSize = CalculatedTextSize };
|
||||
|
||||
@@ -10,7 +10,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
@@ -67,14 +67,14 @@ namespace osu.Game.Graphics.UserInterface
|
||||
box.Colour = colours.Yellow;
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
if (!Active)
|
||||
slideActive();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
if (!Active)
|
||||
slideInactive();
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.EventArgs;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Framework.Input.Events;
|
||||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
|
||||
@@ -33,11 +32,11 @@ namespace osu.Game.Graphics.UserInterface
|
||||
PlaceholderText = "type to search";
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (!state.Keyboard.ControlPressed && !state.Keyboard.ShiftPressed)
|
||||
if (!e.ControlPressed && !e.ShiftPressed)
|
||||
{
|
||||
switch (args.Key)
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Left:
|
||||
case Key.Right:
|
||||
@@ -49,7 +48,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
if (!AllowCommit)
|
||||
{
|
||||
switch (args.Key)
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.KeypadEnter:
|
||||
case Key.Enter:
|
||||
@@ -57,16 +56,16 @@ namespace osu.Game.Graphics.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
if (state.Keyboard.ShiftPressed)
|
||||
if (e.ShiftPressed)
|
||||
{
|
||||
switch (args.Key)
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Delete:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,10 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.EventArgs;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@@ -21,79 +14,17 @@ namespace osu.Game.Graphics.UserInterface
|
||||
/// </summary>
|
||||
public class TriangleButton : OsuButton, IFilterable
|
||||
{
|
||||
private Box hover;
|
||||
|
||||
protected Triangles Triangles;
|
||||
|
||||
public TriangleButton()
|
||||
{
|
||||
Height = 40;
|
||||
}
|
||||
|
||||
protected override SpriteText CreateText() => new OsuSpriteText
|
||||
{
|
||||
Depth = -1,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Font = @"Exo2.0-Bold",
|
||||
};
|
||||
protected Triangles Triangles { get; private set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
BackgroundColour = colours.BlueDark;
|
||||
|
||||
Content.Masking = true;
|
||||
Content.CornerRadius = 5;
|
||||
|
||||
AddRange(new Drawable[]
|
||||
Add(Triangles = new Triangles
|
||||
{
|
||||
Triangles = new Triangles
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ColourDark = colours.BlueDarker,
|
||||
ColourLight = colours.Blue,
|
||||
},
|
||||
hover = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingMode.Additive,
|
||||
Colour = Color4.White.Opacity(0.1f),
|
||||
Alpha = 0,
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ColourDark = colours.BlueDarker,
|
||||
ColourLight = colours.Blue,
|
||||
});
|
||||
|
||||
Enabled.ValueChanged += enabled_ValueChanged;
|
||||
Enabled.TriggerChange();
|
||||
}
|
||||
|
||||
private void enabled_ValueChanged(bool enabled)
|
||||
{
|
||||
this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
hover.FadeIn(200);
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
hover.FadeOut(200);
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
Content.ScaleTo(0.9f, 4000, Easing.OutQuint);
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
Content.ScaleTo(1, 1000, Easing.OutElastic);
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
|
||||
public IEnumerable<string> FilterTerms => new[] { Text };
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user