mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 20:22:55 +08:00
Merge branch 'master' into update-statistics-async
This commit is contained in:
commit
d4c1359a49
@ -9,17 +9,17 @@ cache:
|
|||||||
- inspectcode -> appveyor.yml
|
- inspectcode -> appveyor.yml
|
||||||
- packages -> **\packages.config
|
- packages -> **\packages.config
|
||||||
install:
|
install:
|
||||||
- cmd: git submodule update --init --recursive
|
- cmd: git submodule update --init --recursive --depth=5
|
||||||
- cmd: choco install resharper-clt -y
|
- cmd: choco install resharper-clt -y
|
||||||
- cmd: choco install nvika -y
|
- cmd: choco install nvika -y
|
||||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.3/CodeFileSanity.exe
|
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.3/CodeFileSanity.exe
|
||||||
before_build:
|
before_build:
|
||||||
- cmd: CodeFileSanity.exe
|
- cmd: CodeFileSanity.exe
|
||||||
- cmd: nuget restore
|
- cmd: nuget restore -verbosity quiet
|
||||||
build:
|
build:
|
||||||
project: osu.sln
|
project: osu.sln
|
||||||
parallel: true
|
parallel: true
|
||||||
verbosity: minimal
|
verbosity: minimal
|
||||||
after_build:
|
after_build:
|
||||||
- cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln
|
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
|
||||||
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
@ -1 +1 @@
|
|||||||
Subproject commit d87dab204b3df50f62e6070b1970c135ea647d78
|
Subproject commit 4fc866eee3803f88b155150e32e021b9c21e647f
|
@ -1 +1 @@
|
|||||||
Subproject commit 1750ab8f6761ab35592fd46da71fbe0c141bfd93
|
Subproject commit 4287ee8043fb1419017359bc3a5db5dc06bc643f
|
@ -13,7 +13,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
|||||||
<add key="ProjectName" value="osu.Desktop" />
|
<add key="ProjectName" value="osu.Desktop" />
|
||||||
<add key="NuSpecName" value="osu.Desktop\osu.nuspec" />
|
<add key="NuSpecName" value="osu.Desktop\osu.nuspec" />
|
||||||
<add key="SolutionName" value="osu" />
|
<add key="SolutionName" value="osu" />
|
||||||
<add key="TargetName" value="Client\osu.Desktop" />
|
<add key="TargetName" value="osu.Desktop" />
|
||||||
<add key="PackageName" value="osulazer" />
|
<add key="PackageName" value="osulazer" />
|
||||||
<add key="IconName" value="lazer.ico" />
|
<add key="IconName" value="lazer.ico" />
|
||||||
<add key="CodeSigningCertificate" value="" />
|
<add key="CodeSigningCertificate" value="" />
|
||||||
|
@ -145,6 +145,8 @@ namespace osu.Desktop.Deploy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static void checkReleaseFiles()
|
private static void checkReleaseFiles()
|
||||||
{
|
{
|
||||||
|
if (!canGitHub) return;
|
||||||
|
|
||||||
var releaseLines = getReleaseLines();
|
var releaseLines = getReleaseLines();
|
||||||
|
|
||||||
//ensure we have all files necessary
|
//ensure we have all files necessary
|
||||||
@ -157,6 +159,8 @@ namespace osu.Desktop.Deploy
|
|||||||
|
|
||||||
private static void pruneReleases()
|
private static void pruneReleases()
|
||||||
{
|
{
|
||||||
|
if (!canGitHub) return;
|
||||||
|
|
||||||
write("Pruning RELEASES...");
|
write("Pruning RELEASES...");
|
||||||
|
|
||||||
var releaseLines = getReleaseLines().ToList();
|
var releaseLines = getReleaseLines().ToList();
|
||||||
@ -190,7 +194,7 @@ namespace osu.Desktop.Deploy
|
|||||||
|
|
||||||
private static void uploadBuild(string version)
|
private static void uploadBuild(string version)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(GitHubAccessToken) || string.IsNullOrEmpty(codeSigningCertPath))
|
if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
write("Publishing to GitHub...");
|
write("Publishing to GitHub...");
|
||||||
@ -228,8 +232,12 @@ namespace osu.Desktop.Deploy
|
|||||||
|
|
||||||
private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage);
|
private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage);
|
||||||
|
|
||||||
|
private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken);
|
||||||
|
|
||||||
private static void checkGitHubReleases()
|
private static void checkGitHubReleases()
|
||||||
{
|
{
|
||||||
|
if (!canGitHub) return;
|
||||||
|
|
||||||
write("Checking GitHub releases...");
|
write("Checking GitHub releases...");
|
||||||
var req = new JsonWebRequest<List<GitHubRelease>>($"{GitHubApiEndpoint}");
|
var req = new JsonWebRequest<List<GitHubRelease>>($"{GitHubApiEndpoint}");
|
||||||
req.AuthenticatedBlockingPerform();
|
req.AuthenticatedBlockingPerform();
|
||||||
|
@ -11,14 +11,14 @@ using osu.Game.Rulesets.Objects;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||||
{
|
{
|
||||||
internal class CatchBeatmapConverter : BeatmapConverter<CatchBaseHit>
|
internal class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
|
||||||
{
|
{
|
||||||
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
|
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
|
||||||
|
|
||||||
protected override IEnumerable<CatchBaseHit> ConvertHitObject(HitObject obj, Beatmap beatmap)
|
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap)
|
||||||
{
|
{
|
||||||
var curveData = obj as IHasCurve;
|
var curveData = obj as IHasCurve;
|
||||||
var positionData = obj as IHasPosition;
|
var positionData = obj as IHasXPosition;
|
||||||
var comboData = obj as IHasCombo;
|
var comboData = obj as IHasCombo;
|
||||||
|
|
||||||
if (positionData == null)
|
if (positionData == null)
|
||||||
|
@ -6,9 +6,9 @@ using osu.Game.Rulesets.Catch.Objects;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||||
{
|
{
|
||||||
internal class CatchBeatmapProcessor : BeatmapProcessor<CatchBaseHit>
|
internal class CatchBeatmapProcessor : BeatmapProcessor<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override void PostProcess(Beatmap<CatchBaseHit> beatmap)
|
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
|
||||||
{
|
{
|
||||||
if (beatmap.ComboColors.Count == 0)
|
if (beatmap.ComboColors.Count == 0)
|
||||||
return;
|
return;
|
||||||
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
int comboIndex = 0;
|
int comboIndex = 0;
|
||||||
int colourIndex = 0;
|
int colourIndex = 0;
|
||||||
|
|
||||||
CatchBaseHit lastObj = null;
|
CatchHitObject lastObj = null;
|
||||||
|
|
||||||
foreach (var obj in beatmap.HitObjects)
|
foreach (var obj in beatmap.HitObjects)
|
||||||
{
|
{
|
||||||
|
@ -8,14 +8,14 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch
|
namespace osu.Game.Rulesets.Catch
|
||||||
{
|
{
|
||||||
public class CatchDifficultyCalculator : DifficultyCalculator<CatchBaseHit>
|
public class CatchDifficultyCalculator : DifficultyCalculator<CatchHitObject>
|
||||||
{
|
{
|
||||||
public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap)
|
public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, string> categoryDifficulty = null) => 0;
|
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
|
||||||
|
|
||||||
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
|
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public abstract class CatchBaseHit : HitObject, IHasXPosition, IHasCombo
|
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasCombo
|
||||||
{
|
{
|
||||||
|
public const double OBJECT_RADIUS = 44;
|
||||||
|
|
||||||
public float X { get; set; }
|
public float X { get; set; }
|
||||||
|
|
||||||
public Color4 ComboColour { get; set; } = Color4.Gray;
|
public Color4 ComboColour { get; set; } = Color4.Gray;
|
||||||
@ -20,5 +24,14 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
/// The next fruit starts a new combo. Used for explodey.
|
/// The next fruit starts a new combo. Used for explodey.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool LastInCombo { get; set; }
|
public virtual bool LastInCombo { get; set; }
|
||||||
|
|
||||||
|
public float Scale { get; set; } = 1;
|
||||||
|
|
||||||
|
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||||
|
{
|
||||||
|
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||||
|
|
||||||
|
Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,11 +5,12 @@ using System;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||||
{
|
{
|
||||||
public abstract class DrawableCatchHitObject<TObject> : DrawableCatchHitObject
|
public abstract class DrawableCatchHitObject<TObject> : DrawableCatchHitObject
|
||||||
where TObject : CatchBaseHit
|
where TObject : CatchHitObject
|
||||||
{
|
{
|
||||||
public new TObject HitObject;
|
public new TObject HitObject;
|
||||||
|
|
||||||
@ -17,12 +18,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
HitObject = hitObject;
|
HitObject = hitObject;
|
||||||
|
|
||||||
|
Scale = new Vector2(HitObject.Scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class DrawableCatchHitObject : DrawableScrollingHitObject<CatchBaseHit>
|
public abstract class DrawableCatchHitObject : DrawableScrollingHitObject<CatchHitObject>
|
||||||
{
|
{
|
||||||
protected DrawableCatchHitObject(CatchBaseHit hitObject)
|
protected DrawableCatchHitObject(CatchHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
RelativePositionAxes = Axes.Both;
|
RelativePositionAxes = Axes.Both;
|
||||||
@ -30,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
Y = (float)HitObject.StartTime;
|
Y = (float)HitObject.StartTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Func<CatchBaseHit, bool> CheckPosition;
|
public Func<CatchHitObject, bool> CheckPosition;
|
||||||
|
|
||||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
|
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (CatchBaseHit tick in s.Ticks)
|
foreach (CatchHitObject tick in s.Ticks)
|
||||||
{
|
{
|
||||||
TinyDroplet tiny = tick as TinyDroplet;
|
TinyDroplet tiny = tick as TinyDroplet;
|
||||||
if (tiny != null)
|
if (tiny != null)
|
||||||
@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddNested(DrawableHitObject<CatchBaseHit> h)
|
protected override void AddNested(DrawableHitObject<CatchHitObject> h)
|
||||||
{
|
{
|
||||||
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
||||||
dropletContainer.Add(h);
|
dropletContainer.Add(h);
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
|
|||||||
{
|
{
|
||||||
public class Pulp : Circle, IHasAccentColour
|
public class Pulp : Circle, IHasAccentColour
|
||||||
{
|
{
|
||||||
public const float PULP_SIZE = 20;
|
public const float PULP_SIZE = (float)CatchHitObject.OBJECT_RADIUS / 2.2f;
|
||||||
|
|
||||||
public Pulp()
|
public Pulp()
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class Droplet : CatchBaseHit
|
public class Droplet : CatchHitObject
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class Fruit : CatchBaseHit
|
public class Fruit : CatchHitObject
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ using osu.Framework.Lists;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class JuiceStream : CatchBaseHit, IHasCurve
|
public class JuiceStream : CatchHitObject, IHasCurve
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
||||||
@ -42,11 +42,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<CatchBaseHit> Ticks
|
public IEnumerable<CatchHitObject> Ticks
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
SortedList<CatchBaseHit> ticks = new SortedList<CatchBaseHit>((a, b) => a.StartTime.CompareTo(b.StartTime));
|
SortedList<CatchHitObject> ticks = new SortedList<CatchHitObject>((a, b) => a.StartTime.CompareTo(b.StartTime));
|
||||||
|
|
||||||
if (TickDistance == 0)
|
if (TickDistance == 0)
|
||||||
return ticks;
|
return ticks;
|
||||||
|
@ -10,14 +10,14 @@ using osu.Game.Rulesets.UI;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Scoring
|
namespace osu.Game.Rulesets.Catch.Scoring
|
||||||
{
|
{
|
||||||
internal class CatchScoreProcessor : ScoreProcessor<CatchBaseHit>
|
internal class CatchScoreProcessor : ScoreProcessor<CatchHitObject>
|
||||||
{
|
{
|
||||||
public CatchScoreProcessor(RulesetContainer<CatchBaseHit> rulesetContainer)
|
public CatchScoreProcessor(RulesetContainer<CatchHitObject> rulesetContainer)
|
||||||
: base(rulesetContainer)
|
: base(rulesetContainer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SimulateAutoplay(Beatmap<CatchBaseHit> beatmap)
|
protected override void SimulateAutoplay(Beatmap<CatchHitObject> beatmap)
|
||||||
{
|
{
|
||||||
foreach (var obj in beatmap.HitObjects)
|
foreach (var obj in beatmap.HitObjects)
|
||||||
{
|
{
|
||||||
|
@ -11,16 +11,26 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[Ignore("getting CI working")]
|
[Ignore("getting CI working")]
|
||||||
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
||||||
{
|
{
|
||||||
public TestCaseCatchStacker() : base(typeof(CatchRuleset))
|
public TestCaseCatchStacker()
|
||||||
|
: base(typeof(CatchRuleset))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Beatmap CreateBeatmap()
|
protected override Beatmap CreateBeatmap()
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap();
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo = new BeatmapInfo
|
||||||
|
{
|
||||||
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
|
{
|
||||||
|
CircleSize = 6,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for (int i = 0; i < 256; i++)
|
for (int i = 0; i < 512; i++)
|
||||||
beatmap.HitObjects.Add(new Fruit { X = 0.5f, StartTime = i * 100, NewCombo = i % 8 == 0 });
|
beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
using OpenTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
[Ignore("getting CI working")]
|
|
||||||
internal class TestCaseCatcher : OsuTestCase
|
|
||||||
{
|
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
|
||||||
{
|
|
||||||
typeof(Catcher),
|
|
||||||
};
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(RulesetStore rulesets)
|
|
||||||
{
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new CatchInputManager(rulesets.GetRuleset(2))
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Child = new Catcher
|
|
||||||
{
|
|
||||||
RelativePositionAxes = Axes.Both,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Size = new Vector2(1, 0.2f),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
50
osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs
Normal file
50
osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
[Ignore("getting CI working")]
|
||||||
|
internal class TestCaseCatcherArea : OsuTestCase
|
||||||
|
{
|
||||||
|
private RulesetInfo catchRuleset;
|
||||||
|
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(CatcherArea),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestCaseCatcherArea()
|
||||||
|
{
|
||||||
|
AddSliderStep<float>("CircleSize", 0, 8, 5, createCatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCatcher(float size)
|
||||||
|
{
|
||||||
|
Child = new CatchInputManager(catchRuleset)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new CatcherArea(new BeatmapDifficulty { CircleSize = size })
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.BottomLeft
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(RulesetStore rulesets)
|
||||||
|
{
|
||||||
|
catchRuleset = rulesets.GetRuleset(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs
Normal file
16
osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
[Ignore("getting CI working")]
|
||||||
|
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||||
|
{
|
||||||
|
public TestCasePerformancePoints()
|
||||||
|
: base(new CatchRuleset(new RulesetInfo()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
@ -20,10 +20,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
private readonly Container<Drawable> content;
|
private readonly Container<Drawable> content;
|
||||||
|
|
||||||
private readonly Container catcherContainer;
|
private readonly CatcherArea catcherArea;
|
||||||
private readonly Catcher catcher;
|
|
||||||
|
|
||||||
public CatchPlayfield()
|
public CatchPlayfield(BeatmapDifficulty difficulty)
|
||||||
: base(Axes.Y)
|
: base(Axes.Y)
|
||||||
{
|
{
|
||||||
Container explodingFruitContainer;
|
Container explodingFruitContainer;
|
||||||
@ -43,30 +42,16 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
catcherContainer = new Container
|
catcherArea = new CatcherArea(difficulty)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
ExplodingFruitTarget = explodingFruitContainer,
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
Height = 180,
|
|
||||||
Child = catcher = new Catcher
|
|
||||||
{
|
|
||||||
ExplodingFruitTarget = explodingFruitContainer,
|
|
||||||
RelativePositionAxes = Axes.Both,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
X = 0.5f,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.CanCatch(obj);
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
catcher.Size = new Vector2(catcherContainer.DrawSize.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2;
|
|
||||||
|
|
||||||
public override void Add(DrawableHitObject h)
|
public override void Add(DrawableHitObject h)
|
||||||
{
|
{
|
||||||
@ -88,7 +73,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
(judgedObject.Parent as Container<DrawableHitObject>)?.Remove(judgedObject);
|
(judgedObject.Parent as Container<DrawableHitObject>)?.Remove(judgedObject);
|
||||||
(judgedObject.Parent as Container)?.Remove(judgedObject);
|
(judgedObject.Parent as Container)?.Remove(judgedObject);
|
||||||
|
|
||||||
catcher.Add(judgedObject, screenPosition);
|
catcherArea.Add(judgedObject, screenPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ using osu.Game.Rulesets.UI;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchBaseHit>
|
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchHitObject>
|
||||||
{
|
{
|
||||||
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||||
@ -22,15 +22,15 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
||||||
|
|
||||||
protected override BeatmapProcessor<CatchBaseHit> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
|
protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
|
||||||
|
|
||||||
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new CatchPlayfield();
|
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty);
|
||||||
|
|
||||||
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
|
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
|
||||||
|
|
||||||
protected override DrawableHitObject<CatchBaseHit> GetVisualRepresentation(CatchBaseHit h)
|
protected override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
|
||||||
{
|
{
|
||||||
var fruit = h as Fruit;
|
var fruit = h as Fruit;
|
||||||
if (fruit != null)
|
if (fruit != null)
|
||||||
|
@ -1,193 +0,0 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Graphics.Textures;
|
|
||||||
using osu.Framework.Input.Bindings;
|
|
||||||
using osu.Framework.MathUtils;
|
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using OpenTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
|
||||||
{
|
|
||||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
|
||||||
{
|
|
||||||
private Texture texture;
|
|
||||||
|
|
||||||
private Container<DrawableHitObject> caughtFruit;
|
|
||||||
|
|
||||||
public Container ExplodingFruitTarget;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(TextureStore textures)
|
|
||||||
{
|
|
||||||
texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
createCatcherSprite(),
|
|
||||||
caughtFruit = new Container<DrawableHitObject>
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private int currentDirection;
|
|
||||||
|
|
||||||
private bool dashing;
|
|
||||||
|
|
||||||
protected bool Dashing
|
|
||||||
{
|
|
||||||
get { return dashing; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == dashing) return;
|
|
||||||
|
|
||||||
dashing = value;
|
|
||||||
|
|
||||||
if (dashing)
|
|
||||||
Schedule(addAdditiveSprite);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addAdditiveSprite()
|
|
||||||
{
|
|
||||||
if (!dashing) return;
|
|
||||||
|
|
||||||
var additive = createCatcherSprite();
|
|
||||||
|
|
||||||
additive.RelativePositionAxes = Axes.Both;
|
|
||||||
additive.Blending = BlendingMode.Additive;
|
|
||||||
additive.Position = Position;
|
|
||||||
additive.Scale = Scale;
|
|
||||||
|
|
||||||
((Container)Parent).Add(additive);
|
|
||||||
|
|
||||||
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
|
|
||||||
|
|
||||||
Scheduler.AddDelayed(addAdditiveSprite, 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Sprite createCatcherSprite() => new Sprite
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
FillMode = FillMode.Fit,
|
|
||||||
Texture = texture,
|
|
||||||
OriginPosition = new Vector2(DrawWidth / 2, 10) //temporary until the sprite is aligned correctly.
|
|
||||||
};
|
|
||||||
|
|
||||||
public bool OnPressed(CatchAction action)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case CatchAction.MoveLeft:
|
|
||||||
currentDirection--;
|
|
||||||
return true;
|
|
||||||
case CatchAction.MoveRight:
|
|
||||||
currentDirection++;
|
|
||||||
return true;
|
|
||||||
case CatchAction.Dash:
|
|
||||||
Dashing = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OnReleased(CatchAction action)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case CatchAction.MoveLeft:
|
|
||||||
currentDirection++;
|
|
||||||
return true;
|
|
||||||
case CatchAction.MoveRight:
|
|
||||||
currentDirection--;
|
|
||||||
return true;
|
|
||||||
case CatchAction.Dash:
|
|
||||||
Dashing = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
|
||||||
/// </summary>
|
|
||||||
private const double base_speed = 1.0 / 512;
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
if (currentDirection == 0) return;
|
|
||||||
|
|
||||||
double dashModifier = Dashing ? 1 : 0.5;
|
|
||||||
|
|
||||||
Scale = new Vector2(Math.Sign(currentDirection), 1);
|
|
||||||
X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * base_speed * dashModifier, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
|
|
||||||
{
|
|
||||||
fruit.RelativePositionAxes = Axes.None;
|
|
||||||
fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0);
|
|
||||||
|
|
||||||
fruit.Anchor = Anchor.TopCentre;
|
|
||||||
fruit.Origin = Anchor.BottomCentre;
|
|
||||||
fruit.Scale *= 0.7f;
|
|
||||||
fruit.LifetimeEnd = double.MaxValue;
|
|
||||||
|
|
||||||
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
|
|
||||||
|
|
||||||
while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
|
|
||||||
{
|
|
||||||
fruit.X += RNG.Next(-5, 5);
|
|
||||||
fruit.Y -= RNG.Next(0, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
caughtFruit.Add(fruit);
|
|
||||||
|
|
||||||
if (((CatchBaseHit)fruit.HitObject).LastInCombo)
|
|
||||||
explode();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void explode()
|
|
||||||
{
|
|
||||||
var fruit = caughtFruit.ToArray();
|
|
||||||
|
|
||||||
foreach (var f in fruit)
|
|
||||||
{
|
|
||||||
var originalX = f.X * Scale.X;
|
|
||||||
|
|
||||||
if (ExplodingFruitTarget != null)
|
|
||||||
{
|
|
||||||
f.Anchor = Anchor.TopLeft;
|
|
||||||
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
|
||||||
|
|
||||||
caughtFruit.Remove(f);
|
|
||||||
|
|
||||||
ExplodingFruitTarget.Add(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
|
|
||||||
.Then()
|
|
||||||
.MoveToY(f.Y + 50, 500, Easing.InSine);
|
|
||||||
|
|
||||||
f.MoveToX(f.X + originalX * 6, 1000);
|
|
||||||
f.FadeOut(750);
|
|
||||||
|
|
||||||
f.Expire();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
240
osu.Game.Rulesets.Catch/UI/CatcherArea.cs
Normal file
240
osu.Game.Rulesets.Catch/UI/CatcherArea.cs
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
|
{
|
||||||
|
public class CatcherArea : Container
|
||||||
|
{
|
||||||
|
public const float CATCHER_SIZE = 172;
|
||||||
|
|
||||||
|
private readonly Catcher catcher;
|
||||||
|
|
||||||
|
public Container ExplodingFruitTarget
|
||||||
|
{
|
||||||
|
set { catcher.ExplodingFruitTarget = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public CatcherArea(BeatmapDifficulty difficulty = null)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Height = CATCHER_SIZE;
|
||||||
|
Child = catcher = new Catcher(difficulty)
|
||||||
|
{
|
||||||
|
AdditiveTarget = this,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
|
||||||
|
{
|
||||||
|
fruit.RelativePositionAxes = Axes.None;
|
||||||
|
fruit.Position = new Vector2(catcher.ToLocalSpace(absolutePosition).X - catcher.DrawSize.X / 2, 0);
|
||||||
|
|
||||||
|
fruit.Anchor = Anchor.TopCentre;
|
||||||
|
fruit.Origin = Anchor.BottomCentre;
|
||||||
|
fruit.Scale *= 0.7f;
|
||||||
|
fruit.LifetimeEnd = double.MaxValue;
|
||||||
|
|
||||||
|
catcher.Add(fruit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanCatch(CatchHitObject obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X * Math.Abs(catcher.Scale.X) / DrawSize.X / 2;
|
||||||
|
|
||||||
|
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||||
|
{
|
||||||
|
private Texture texture;
|
||||||
|
|
||||||
|
private Container<DrawableHitObject> caughtFruit;
|
||||||
|
|
||||||
|
public Container ExplodingFruitTarget;
|
||||||
|
|
||||||
|
public Container AdditiveTarget;
|
||||||
|
|
||||||
|
public Catcher(BeatmapDifficulty difficulty = null)
|
||||||
|
{
|
||||||
|
RelativePositionAxes = Axes.X;
|
||||||
|
X = 0.5f;
|
||||||
|
|
||||||
|
Origin = Anchor.TopCentre;
|
||||||
|
Anchor = Anchor.TopLeft;
|
||||||
|
|
||||||
|
Size = new Vector2(CATCHER_SIZE);
|
||||||
|
if (difficulty != null)
|
||||||
|
Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(TextureStore textures)
|
||||||
|
{
|
||||||
|
texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
createCatcherSprite(),
|
||||||
|
caughtFruit = new Container<DrawableHitObject>
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private int currentDirection;
|
||||||
|
|
||||||
|
private bool dashing;
|
||||||
|
|
||||||
|
protected bool Dashing
|
||||||
|
{
|
||||||
|
get { return dashing; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == dashing) return;
|
||||||
|
|
||||||
|
dashing = value;
|
||||||
|
|
||||||
|
if (dashing)
|
||||||
|
Schedule(addAdditiveSprite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAdditiveSprite()
|
||||||
|
{
|
||||||
|
if (!dashing || AdditiveTarget == null) return;
|
||||||
|
|
||||||
|
var additive = createCatcherSprite();
|
||||||
|
|
||||||
|
additive.Anchor = Anchor;
|
||||||
|
additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly.
|
||||||
|
additive.Position = Position;
|
||||||
|
additive.Scale = Scale;
|
||||||
|
additive.RelativePositionAxes = RelativePositionAxes;
|
||||||
|
additive.Blending = BlendingMode.Additive;
|
||||||
|
|
||||||
|
AdditiveTarget.Add(additive);
|
||||||
|
|
||||||
|
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
|
||||||
|
|
||||||
|
Scheduler.AddDelayed(addAdditiveSprite, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Sprite createCatcherSprite() => new Sprite
|
||||||
|
{
|
||||||
|
Size = new Vector2(CATCHER_SIZE),
|
||||||
|
FillMode = FillMode.Fill,
|
||||||
|
Texture = texture,
|
||||||
|
OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly.
|
||||||
|
};
|
||||||
|
|
||||||
|
public void Add(DrawableHitObject fruit)
|
||||||
|
{
|
||||||
|
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
|
||||||
|
|
||||||
|
while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
|
||||||
|
{
|
||||||
|
fruit.X += RNG.Next(-5, 5);
|
||||||
|
fruit.Y -= RNG.Next(0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
caughtFruit.Add(fruit);
|
||||||
|
|
||||||
|
if (((CatchHitObject)fruit.HitObject).LastInCombo)
|
||||||
|
explode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(CatchAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case CatchAction.MoveLeft:
|
||||||
|
currentDirection--;
|
||||||
|
return true;
|
||||||
|
case CatchAction.MoveRight:
|
||||||
|
currentDirection++;
|
||||||
|
return true;
|
||||||
|
case CatchAction.Dash:
|
||||||
|
Dashing = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(CatchAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case CatchAction.MoveLeft:
|
||||||
|
currentDirection++;
|
||||||
|
return true;
|
||||||
|
case CatchAction.MoveRight:
|
||||||
|
currentDirection--;
|
||||||
|
return true;
|
||||||
|
case CatchAction.Dash:
|
||||||
|
Dashing = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
||||||
|
/// </summary>
|
||||||
|
public const double BASE_SPEED = 1.0 / 512;
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (currentDirection == 0) return;
|
||||||
|
|
||||||
|
double dashModifier = Dashing ? 1 : 0.5;
|
||||||
|
|
||||||
|
Scale = new Vector2(Math.Abs(Scale.X) * Math.Sign(currentDirection), Scale.Y);
|
||||||
|
X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void explode()
|
||||||
|
{
|
||||||
|
var fruit = caughtFruit.ToArray();
|
||||||
|
|
||||||
|
foreach (var f in fruit)
|
||||||
|
{
|
||||||
|
var originalX = f.X * Scale.X;
|
||||||
|
|
||||||
|
if (ExplodingFruitTarget != null)
|
||||||
|
{
|
||||||
|
f.Anchor = Anchor.TopLeft;
|
||||||
|
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
||||||
|
|
||||||
|
caughtFruit.Remove(f);
|
||||||
|
|
||||||
|
ExplodingFruitTarget.Add(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
|
||||||
|
.Then()
|
||||||
|
.MoveToY(f.Y + 50, 500, Easing.InSine);
|
||||||
|
|
||||||
|
f.MoveToX(f.X + originalX * 6, 1000);
|
||||||
|
f.FadeOut(750);
|
||||||
|
|
||||||
|
f.Expire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -57,16 +57,17 @@
|
|||||||
<Compile Include="Objects\JuiceStream.cs" />
|
<Compile Include="Objects\JuiceStream.cs" />
|
||||||
<Compile Include="Scoring\CatchScoreProcessor.cs" />
|
<Compile Include="Scoring\CatchScoreProcessor.cs" />
|
||||||
<Compile Include="Judgements\CatchJudgement.cs" />
|
<Compile Include="Judgements\CatchJudgement.cs" />
|
||||||
<Compile Include="Objects\CatchBaseHit.cs" />
|
<Compile Include="Objects\CatchHitObject.cs" />
|
||||||
<Compile Include="Objects\Drawable\DrawableFruit.cs" />
|
<Compile Include="Objects\Drawable\DrawableFruit.cs" />
|
||||||
<Compile Include="Objects\Droplet.cs" />
|
<Compile Include="Objects\Droplet.cs" />
|
||||||
<Compile Include="Objects\Fruit.cs" />
|
<Compile Include="Objects\Fruit.cs" />
|
||||||
<Compile Include="Objects\TinyDroplet.cs" />
|
<Compile Include="Objects\TinyDroplet.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Tests\TestCaseCatcher.cs" />
|
<Compile Include="Tests\TestCaseCatcherArea.cs" />
|
||||||
<Compile Include="Tests\TestCaseCatchStacker.cs" />
|
<Compile Include="Tests\TestCaseCatchStacker.cs" />
|
||||||
|
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||||
<Compile Include="Tests\TestCaseCatchPlayer.cs" />
|
<Compile Include="Tests\TestCaseCatchPlayer.cs" />
|
||||||
<Compile Include="UI\Catcher.cs" />
|
<Compile Include="UI\CatcherArea.cs" />
|
||||||
<Compile Include="UI\CatchRulesetContainer.cs" />
|
<Compile Include="UI\CatchRulesetContainer.cs" />
|
||||||
<Compile Include="UI\CatchPlayfield.cs" />
|
<Compile Include="UI\CatchPlayfield.cs" />
|
||||||
<Compile Include="CatchRuleset.cs" />
|
<Compile Include="CatchRuleset.cs" />
|
||||||
|
@ -138,8 +138,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
Pattern newPattern = conversion.Generate();
|
Pattern newPattern = conversion.Generate();
|
||||||
lastPattern = newPattern;
|
lastPattern = newPattern;
|
||||||
|
|
||||||
var stairPatternGenerator = (HitObjectPatternGenerator)conversion;
|
var stairPatternGenerator = conversion as HitObjectPatternGenerator;
|
||||||
lastStair = stairPatternGenerator.StairType;
|
lastStair = stairPatternGenerator?.StairType ?? lastStair;
|
||||||
|
|
||||||
return newPattern.HitObjects;
|
return newPattern.HitObjects;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, string> categoryDifficulty = null) => 0;
|
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
|
||||||
|
|
||||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize)));
|
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize)));
|
||||||
}
|
}
|
||||||
|
@ -176,22 +176,10 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
|
|
||||||
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
|
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
|
||||||
{
|
{
|
||||||
private int availableColumns;
|
|
||||||
|
|
||||||
public override void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
|
|
||||||
{
|
|
||||||
// Todo: This shouldn't be done, we should be getting a ManiaBeatmap which should store AvailableColumns
|
|
||||||
// But this is dependent on a _lot_ of refactoring
|
|
||||||
var maniaRulesetContainer = (ManiaRulesetContainer)rulesetContainer;
|
|
||||||
availableColumns = maniaRulesetContainer.AvailableColumns;
|
|
||||||
|
|
||||||
base.ApplyToRulesetContainer(rulesetContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap) => new Score
|
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap) => new Score
|
||||||
{
|
{
|
||||||
User = new User { Username = "osu!topus!" },
|
User = new User { Username = "osu!topus!" },
|
||||||
Replay = new ManiaAutoGenerator(beatmap, availableColumns).Generate(),
|
Replay = new ManiaAutoGenerator(beatmap).Generate(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
@ -13,15 +13,11 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
{
|
{
|
||||||
internal class ManiaAutoGenerator : AutoGenerator<ManiaHitObject>
|
internal class ManiaAutoGenerator : AutoGenerator<ManiaHitObject>
|
||||||
{
|
{
|
||||||
private const double release_delay = 20;
|
public const double RELEASE_DELAY = 20;
|
||||||
|
|
||||||
private readonly int availableColumns;
|
public ManiaAutoGenerator(Beatmap<ManiaHitObject> beatmap)
|
||||||
|
|
||||||
public ManiaAutoGenerator(Beatmap<ManiaHitObject> beatmap, int availableColumns)
|
|
||||||
: base(beatmap)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
this.availableColumns = availableColumns;
|
|
||||||
|
|
||||||
Replay = new Replay { User = new User { Username = @"Autoplay" } };
|
Replay = new Replay { User = new User { Username = @"Autoplay" } };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,104 +26,52 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
public override Replay Generate()
|
public override Replay Generate()
|
||||||
{
|
{
|
||||||
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
|
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
|
||||||
Replay.Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None));
|
Replay.Frames.Add(new ManiaReplayFrame(-100000, 0));
|
||||||
|
|
||||||
double[] holdEndTimes = new double[availableColumns];
|
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
|
||||||
for (int i = 0; i < availableColumns; i++)
|
|
||||||
holdEndTimes[i] = double.NegativeInfinity;
|
|
||||||
|
|
||||||
// Notes are handled row-by-row
|
int activeColumns = 0;
|
||||||
foreach (var objGroup in Beatmap.HitObjects.GroupBy(h => h.StartTime))
|
foreach (var group in pointGroups)
|
||||||
{
|
{
|
||||||
double groupTime = objGroup.Key;
|
foreach (var point in group)
|
||||||
|
|
||||||
int activeColumns = 0;
|
|
||||||
|
|
||||||
// Get the previously held-down active columns
|
|
||||||
for (int i = 0; i < availableColumns; i++)
|
|
||||||
{
|
{
|
||||||
if (holdEndTimes[i] > groupTime)
|
if (point is HitPoint)
|
||||||
activeColumns |= 1 << i;
|
activeColumns |= 1 << point.Column;
|
||||||
|
if (point is ReleasePoint)
|
||||||
|
activeColumns ^= 1 << point.Column;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add on the group columns, keeping track of the held notes for the next rows
|
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, activeColumns));
|
||||||
foreach (var obj in objGroup)
|
|
||||||
{
|
|
||||||
var holdNote = obj as HoldNote;
|
|
||||||
if (holdNote != null)
|
|
||||||
holdEndTimes[obj.Column] = Math.Max(holdEndTimes[obj.Column], holdNote.EndTime);
|
|
||||||
|
|
||||||
activeColumns |= 1 << obj.Column;
|
|
||||||
}
|
|
||||||
|
|
||||||
Replay.Frames.Add(new ReplayFrame(groupTime, activeColumns, null, ReplayButtonState.None));
|
|
||||||
|
|
||||||
// Add the release frames. We can't do this with the loop above because we need activeColumns to be fully populated
|
|
||||||
foreach (var obj in objGroup.GroupBy(h => (h as IHasEndTime)?.EndTime ?? h.StartTime + release_delay).OrderBy(h => h.Key))
|
|
||||||
{
|
|
||||||
var groupEndTime = obj.Key;
|
|
||||||
|
|
||||||
int activeColumnsAtEnd = 0;
|
|
||||||
for (int i = 0; i < availableColumns; i++)
|
|
||||||
{
|
|
||||||
if (holdEndTimes[i] > groupEndTime)
|
|
||||||
activeColumnsAtEnd |= 1 << i;
|
|
||||||
}
|
|
||||||
|
|
||||||
Replay.Frames.Add(new ReplayFrame(groupEndTime, activeColumnsAtEnd, 0, ReplayButtonState.None));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Replay.Frames = Replay.Frames
|
|
||||||
// Pick the maximum activeColumns for all frames at the same time
|
|
||||||
.GroupBy(f => f.Time)
|
|
||||||
.Select(g => new ReplayFrame(g.First().Time, maxMouseX(g), 0, ReplayButtonState.None))
|
|
||||||
// The addition of release frames above maybe result in unordered frames, but we need them ordered
|
|
||||||
.OrderBy(f => f.Time)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return Replay;
|
return Replay;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private IEnumerable<IActionPoint> generateActionPoints()
|
||||||
/// Finds the maximum <see cref="ReplayFrame.MouseX"/> by count of bits from a grouping of <see cref="ReplayFrame"/>s.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="group">The <see cref="ReplayFrame"/> grouping to search.</param>
|
|
||||||
/// <returns>The maximum <see cref="ReplayFrame.MouseX"/> by count of bits.</returns>
|
|
||||||
private float maxMouseX(IGrouping<double, ReplayFrame> group)
|
|
||||||
{
|
{
|
||||||
int currentCount = -1;
|
foreach (var obj in Beatmap.HitObjects)
|
||||||
int currentMax = 0;
|
|
||||||
|
|
||||||
foreach (var val in group)
|
|
||||||
{
|
{
|
||||||
int newCount = countBits((int)(val.MouseX ?? 0));
|
yield return new HitPoint { Time = obj.StartTime, Column = obj.Column };
|
||||||
if (newCount > currentCount)
|
yield return new ReleasePoint { Time = ((obj as IHasEndTime)?.EndTime ?? obj.StartTime) + RELEASE_DELAY, Column = obj.Column };
|
||||||
{
|
|
||||||
currentCount = newCount;
|
|
||||||
currentMax = (int)(val.MouseX ?? 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentMax;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private interface IActionPoint
|
||||||
/// Counts the number of bits set in a value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to count.</param>
|
|
||||||
/// <returns>The number of set bits.</returns>
|
|
||||||
private int countBits(int value)
|
|
||||||
{
|
{
|
||||||
int count = 0;
|
double Time { get; set; }
|
||||||
while (value > 0)
|
int Column { get; set; }
|
||||||
{
|
}
|
||||||
if ((value & 1) > 0)
|
|
||||||
count++;
|
|
||||||
value >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
private struct HitPoint : IActionPoint
|
||||||
|
{
|
||||||
|
public double Time { get; set; }
|
||||||
|
public int Column { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ReleasePoint : IActionPoint
|
||||||
|
{
|
||||||
|
public double Time { get; set; }
|
||||||
|
public int Column { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,29 +2,37 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Replays
|
namespace osu.Game.Rulesets.Mania.Replays
|
||||||
{
|
{
|
||||||
internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler
|
internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler
|
||||||
{
|
{
|
||||||
public ManiaFramedReplayInputHandler(Replay replay)
|
private readonly ManiaRulesetContainer container;
|
||||||
|
|
||||||
|
public ManiaFramedReplayInputHandler(Replay replay, ManiaRulesetContainer container)
|
||||||
: base(replay)
|
: base(replay)
|
||||||
{
|
{
|
||||||
|
this.container = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ManiaPlayfield playfield;
|
||||||
public override List<InputState> GetPendingStates()
|
public override List<InputState> GetPendingStates()
|
||||||
{
|
{
|
||||||
var actions = new List<ManiaAction>();
|
var actions = new List<ManiaAction>();
|
||||||
|
|
||||||
int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
|
if (playfield == null)
|
||||||
|
playfield = (ManiaPlayfield)container.Playfield;
|
||||||
|
|
||||||
|
int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
while (activeColumns > 0)
|
while (activeColumns > 0)
|
||||||
{
|
{
|
||||||
if ((activeColumns & 1) > 0)
|
if ((activeColumns & 1) > 0)
|
||||||
actions.Add(ManiaAction.Key1 + counter);
|
actions.Add(playfield.Columns.ElementAt(counter).Action);
|
||||||
counter++;
|
counter++;
|
||||||
activeColumns >>= 1;
|
activeColumns >>= 1;
|
||||||
}
|
}
|
||||||
|
17
osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
Normal file
17
osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Replays
|
||||||
|
{
|
||||||
|
public class ManiaReplayFrame : ReplayFrame
|
||||||
|
{
|
||||||
|
public override bool IsImportant => MouseX > 0;
|
||||||
|
|
||||||
|
public ManiaReplayFrame(double time, int activeColumns)
|
||||||
|
: base(time, activeColumns, null, ReplayButtonState.None)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
173
osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs
Normal file
173
osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
[Ignore("getting CI working")]
|
||||||
|
public class TestCaseAutoGeneration : OsuTestCase
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestSingleNote()
|
||||||
|
{
|
||||||
|
// | |
|
||||||
|
// | - |
|
||||||
|
// | |
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>();
|
||||||
|
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||||
|
|
||||||
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
|
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||||
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||||
|
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||||
|
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed");
|
||||||
|
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSingleHoldNote()
|
||||||
|
{
|
||||||
|
// | |
|
||||||
|
// | * |
|
||||||
|
// | * |
|
||||||
|
// | * |
|
||||||
|
// | |
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>();
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||||
|
|
||||||
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
|
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||||
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||||
|
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||||
|
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed");
|
||||||
|
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSingleNoteChord()
|
||||||
|
{
|
||||||
|
// | | |
|
||||||
|
// | - | - |
|
||||||
|
// | | |
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>();
|
||||||
|
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||||
|
beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 });
|
||||||
|
|
||||||
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
|
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||||
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||||
|
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||||
|
Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed");
|
||||||
|
Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHoldNoteChord()
|
||||||
|
{
|
||||||
|
// | | |
|
||||||
|
// | * | * |
|
||||||
|
// | * | * |
|
||||||
|
// | * | * |
|
||||||
|
// | | |
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>();
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 });
|
||||||
|
|
||||||
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
|
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||||
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||||
|
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||||
|
Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed");
|
||||||
|
Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSingleNoteStair()
|
||||||
|
{
|
||||||
|
// | | |
|
||||||
|
// | | - |
|
||||||
|
// | - | |
|
||||||
|
// | | |
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>();
|
||||||
|
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||||
|
beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 });
|
||||||
|
|
||||||
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
|
Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
|
||||||
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
||||||
|
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time");
|
||||||
|
Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time");
|
||||||
|
Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
|
||||||
|
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
|
||||||
|
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 1 has not been released");
|
||||||
|
Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 2 has not been pressed");
|
||||||
|
Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHoldNoteStair()
|
||||||
|
{
|
||||||
|
// | | |
|
||||||
|
// | | * |
|
||||||
|
// | * | * |
|
||||||
|
// | * | * |
|
||||||
|
// | * | |
|
||||||
|
// | | |
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>();
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 });
|
||||||
|
|
||||||
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
|
Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
|
||||||
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
||||||
|
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time");
|
||||||
|
Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time");
|
||||||
|
Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
|
||||||
|
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
|
||||||
|
Assert.AreEqual(3, generated.Frames[2].MouseX, "Keys 1 and 2 have not been pressed");
|
||||||
|
Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 1 has not been released");
|
||||||
|
Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHoldNoteWithReleasePress()
|
||||||
|
{
|
||||||
|
// | | |
|
||||||
|
// | * | - |
|
||||||
|
// | * | |
|
||||||
|
// | * | |
|
||||||
|
// | | |
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>();
|
||||||
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY });
|
||||||
|
beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
|
||||||
|
|
||||||
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
|
Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames");
|
||||||
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
||||||
|
Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time");
|
||||||
|
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time");
|
||||||
|
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
|
||||||
|
Assert.AreEqual(2, generated.Frames[2].MouseX, "Key 1 has not been released or key 2 has not been pressed");
|
||||||
|
Assert.AreEqual(0, generated.Frames[3].MouseX, "Keys 1 and 2 have not been released");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs
Normal file
16
osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
[Ignore("getting CI working")]
|
||||||
|
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||||
|
{
|
||||||
|
public TestCasePerformancePoints()
|
||||||
|
: base(new ManiaRuleset(new RulesetInfo()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -124,6 +124,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
|
protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
|
||||||
|
|
||||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
|
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
<Compile Include="Objects\Types\IHasColumn.cs" />
|
<Compile Include="Objects\Types\IHasColumn.cs" />
|
||||||
<Compile Include="Replays\ManiaAutoGenerator.cs" />
|
<Compile Include="Replays\ManiaAutoGenerator.cs" />
|
||||||
<Compile Include="Replays\ManiaFramedReplayInputHandler.cs" />
|
<Compile Include="Replays\ManiaFramedReplayInputHandler.cs" />
|
||||||
|
<Compile Include="Replays\ManiaReplayFrame.cs" />
|
||||||
<Compile Include="Scoring\ManiaScoreProcessor.cs" />
|
<Compile Include="Scoring\ManiaScoreProcessor.cs" />
|
||||||
<Compile Include="Objects\BarLine.cs" />
|
<Compile Include="Objects\BarLine.cs" />
|
||||||
<Compile Include="Objects\HoldNote.cs" />
|
<Compile Include="Objects\HoldNote.cs" />
|
||||||
@ -80,8 +81,10 @@
|
|||||||
<Compile Include="Objects\Note.cs" />
|
<Compile Include="Objects\Note.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ManiaInputManager.cs" />
|
<Compile Include="ManiaInputManager.cs" />
|
||||||
|
<Compile Include="Tests\TestCaseAutoGeneration.cs" />
|
||||||
<Compile Include="Tests\TestCaseManiaHitObjects.cs" />
|
<Compile Include="Tests\TestCaseManiaHitObjects.cs" />
|
||||||
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
|
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
|
||||||
|
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||||
<Compile Include="Timing\GravityScrollingContainer.cs" />
|
<Compile Include="Timing\GravityScrollingContainer.cs" />
|
||||||
<Compile Include="Timing\ScrollingAlgorithm.cs" />
|
<Compile Include="Timing\ScrollingAlgorithm.cs" />
|
||||||
<Compile Include="UI\Column.cs" />
|
<Compile Include="UI\Column.cs" />
|
||||||
|
@ -45,6 +45,18 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
set { Curve.Distance = value; }
|
set { Curve.Distance = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The position of the cursor at the point of completion of this <see cref="Slider"/> if it was hit
|
||||||
|
/// with as few movements as possible. This is set and used by difficulty calculation.
|
||||||
|
/// </summary>
|
||||||
|
internal Vector2? LazyEndPosition;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The distance travelled by the cursor upon completion of this <see cref="Slider"/> if it was hit
|
||||||
|
/// with as few movements as possible. This is set and used by difficulty calculation.
|
||||||
|
/// </summary>
|
||||||
|
internal float LazyTravelDistance;
|
||||||
|
|
||||||
public List<SampleInfoList> RepeatSamples { get; set; } = new List<SampleInfoList>();
|
public List<SampleInfoList> RepeatSamples { get; set; } = new List<SampleInfoList>();
|
||||||
public int RepeatCount { get; set; } = 1;
|
public int RepeatCount { get; set; } = 1;
|
||||||
|
|
||||||
|
@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
|
|||||||
(h as Slider)?.Curve?.Calculate();
|
(h as Slider)?.Curve?.Calculate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, string> categoryDifficulty = null)
|
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||||
{
|
{
|
||||||
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects);
|
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects, TimeRate);
|
||||||
Skill[] skills =
|
Skill[] skills =
|
||||||
{
|
{
|
||||||
new Aim(),
|
new Aim(),
|
||||||
@ -67,8 +67,8 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
|
|||||||
|
|
||||||
if (categoryDifficulty != null)
|
if (categoryDifficulty != null)
|
||||||
{
|
{
|
||||||
categoryDifficulty.Add("Aim", aimRating.ToString("0.00"));
|
categoryDifficulty.Add("Aim", aimRating);
|
||||||
categoryDifficulty.Add("Speed", speedRating.ToString("0.00"));
|
categoryDifficulty.Add("Speed", speedRating);
|
||||||
}
|
}
|
||||||
|
|
||||||
return starRating;
|
return starRating;
|
||||||
|
@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
|||||||
/// Creates an enumerator, which preprocesses a list of <see cref="OsuHitObject"/>s recieved as input, wrapping them as
|
/// Creates an enumerator, which preprocesses a list of <see cref="OsuHitObject"/>s recieved as input, wrapping them as
|
||||||
/// <see cref="OsuDifficultyHitObject"/> which contains extra data required for difficulty calculation.
|
/// <see cref="OsuDifficultyHitObject"/> which contains extra data required for difficulty calculation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public OsuDifficultyBeatmap(List<OsuHitObject> objects)
|
public OsuDifficultyBeatmap(List<OsuHitObject> objects, double timeRate)
|
||||||
{
|
{
|
||||||
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
|
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
|
||||||
// This should probably happen before the objects reach the difficulty calculator.
|
// This should probably happen before the objects reach the difficulty calculator.
|
||||||
objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
|
objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
|
||||||
difficultyObjects = createDifficultyObjectEnumerator(objects);
|
difficultyObjects = createDifficultyObjectEnumerator(objects, timeRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
|||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
private IEnumerator<OsuDifficultyHitObject> createDifficultyObjectEnumerator(List<OsuHitObject> objects)
|
private IEnumerator<OsuDifficultyHitObject> createDifficultyObjectEnumerator(List<OsuHitObject> objects, double timeRate)
|
||||||
{
|
{
|
||||||
// We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object.
|
// We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object.
|
||||||
OsuHitObject[] triangle = new OsuHitObject[3];
|
OsuHitObject[] triangle = new OsuHitObject[3];
|
||||||
@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
|||||||
triangle[1] = triangle[0];
|
triangle[1] = triangle[0];
|
||||||
triangle[0] = objects[i];
|
triangle[0] = objects[i];
|
||||||
|
|
||||||
yield return new OsuDifficultyHitObject(triangle);
|
yield return new OsuDifficultyHitObject(triangle, timeRate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenTK;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||||
@ -33,13 +35,17 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
|||||||
|
|
||||||
private const int normalized_radius = 52;
|
private const int normalized_radius = 52;
|
||||||
|
|
||||||
|
private readonly double timeRate;
|
||||||
|
|
||||||
private readonly OsuHitObject[] t;
|
private readonly OsuHitObject[] t;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the object calculating extra data required for difficulty calculation.
|
/// Initializes the object calculating extra data required for difficulty calculation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public OsuDifficultyHitObject(OsuHitObject[] triangle)
|
public OsuDifficultyHitObject(OsuHitObject[] triangle, double timeRate)
|
||||||
{
|
{
|
||||||
|
this.timeRate = timeRate;
|
||||||
|
|
||||||
t = triangle;
|
t = triangle;
|
||||||
BaseObject = t[0];
|
BaseObject = t[0];
|
||||||
setDistances();
|
setDistances();
|
||||||
@ -57,14 +63,53 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
|||||||
scalingFactor *= 1 + smallCircleBonus;
|
scalingFactor *= 1 + smallCircleBonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
Distance = (t[0].StackedPosition - t[1].StackedPosition).Length * scalingFactor;
|
Vector2 lastCursorPosition = t[1].StackedPosition;
|
||||||
|
float lastTravelDistance = 0;
|
||||||
|
|
||||||
|
var lastSlider = t[1] as Slider;
|
||||||
|
if (lastSlider != null)
|
||||||
|
{
|
||||||
|
computeSliderCursorPosition(lastSlider);
|
||||||
|
lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition;
|
||||||
|
lastTravelDistance = lastSlider.LazyTravelDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Distance = (lastTravelDistance + (BaseObject.StackedPosition - lastCursorPosition).Length) * scalingFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setTimingValues()
|
private void setTimingValues()
|
||||||
{
|
{
|
||||||
// Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure.
|
// Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure.
|
||||||
DeltaTime = Math.Max(40, t[0].StartTime - t[1].StartTime);
|
DeltaTime = Math.Max(40, (t[0].StartTime - t[1].StartTime) / timeRate);
|
||||||
TimeUntilHit = 450; // BaseObject.PreEmpt;
|
TimeUntilHit = 450; // BaseObject.PreEmpt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void computeSliderCursorPosition(Slider slider)
|
||||||
|
{
|
||||||
|
if (slider.LazyEndPosition != null)
|
||||||
|
return;
|
||||||
|
slider.LazyEndPosition = slider.StackedPosition;
|
||||||
|
|
||||||
|
float approxFollowCircleRadius = (float)(slider.Radius * 3);
|
||||||
|
var computeVertex = new Action<double>(t =>
|
||||||
|
{
|
||||||
|
var diff = slider.PositionAt(t) - slider.LazyEndPosition.Value;
|
||||||
|
float dist = diff.Length;
|
||||||
|
|
||||||
|
if (dist > approxFollowCircleRadius)
|
||||||
|
{
|
||||||
|
// The cursor would be outside the follow circle, we need to move it
|
||||||
|
diff.Normalize(); // Obtain direction of diff
|
||||||
|
dist -= approxFollowCircleRadius;
|
||||||
|
slider.LazyEndPosition += diff * dist;
|
||||||
|
slider.LazyTravelDistance += dist;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var scoringTimes = slider.Ticks.Select(t => t.StartTime).Concat(slider.RepeatPoints.Select(r => r.StartTime)).OrderBy(t => t);
|
||||||
|
foreach (var time in scoringTimes)
|
||||||
|
computeVertex(time);
|
||||||
|
computeVertex(slider.EndTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ using System.Linq;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu
|
namespace osu.Game.Rulesets.Osu
|
||||||
{
|
{
|
||||||
@ -114,6 +116,8 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
|
|
||||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods);
|
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods);
|
||||||
|
|
||||||
|
public override PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score);
|
||||||
|
|
||||||
public override string Description => "osu!";
|
public override string Description => "osu!";
|
||||||
|
|
||||||
public override SettingsSubsection CreateSettings() => new OsuSettings();
|
public override SettingsSubsection CreateSettings() => new OsuSettings();
|
||||||
|
199
osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs
Normal file
199
osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Scoring
|
||||||
|
{
|
||||||
|
public class OsuPerformanceCalculator : PerformanceCalculator<OsuHitObject>
|
||||||
|
{
|
||||||
|
private readonly int countHitCircles;
|
||||||
|
private readonly int beatmapMaxCombo;
|
||||||
|
|
||||||
|
private Mod[] mods;
|
||||||
|
private double realApproachRate;
|
||||||
|
private double accuracy;
|
||||||
|
private int scoreMaxCombo;
|
||||||
|
private int count300;
|
||||||
|
private int count100;
|
||||||
|
private int count50;
|
||||||
|
private int countMiss;
|
||||||
|
|
||||||
|
public OsuPerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score)
|
||||||
|
: base(ruleset, beatmap, score)
|
||||||
|
{
|
||||||
|
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
|
||||||
|
|
||||||
|
beatmapMaxCombo = Beatmap.HitObjects.Count;
|
||||||
|
beatmapMaxCombo += Beatmap.HitObjects.OfType<Slider>().Sum(s => s.RepeatCount + s.Ticks.Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override double Calculate(Dictionary<string, double> categoryRatings = null)
|
||||||
|
{
|
||||||
|
mods = Score.Mods;
|
||||||
|
accuracy = Score.Accuracy;
|
||||||
|
scoreMaxCombo = Score.MaxCombo;
|
||||||
|
count300 = Convert.ToInt32(Score.Statistics["300"]);
|
||||||
|
count100 = Convert.ToInt32(Score.Statistics["100"]);
|
||||||
|
count50 = Convert.ToInt32(Score.Statistics["50"]);
|
||||||
|
countMiss = Convert.ToInt32(Score.Statistics["x"]);
|
||||||
|
|
||||||
|
// Don't count scores made with supposedly unranked mods
|
||||||
|
if (mods.Any(m => !m.Ranked))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Todo: In the future we should apply changes to PreEmpt/AR at an OsuHitObject/BaseDifficulty level, but this is done
|
||||||
|
// locally for now as doing so would modify animations and other things unexpectedly
|
||||||
|
// DO NOT MODIFY THIS
|
||||||
|
double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate;
|
||||||
|
if (mods.Any(m => m is OsuModHardRock))
|
||||||
|
ar = Math.Min(10, ar * 1.4);
|
||||||
|
if (mods.Any(m => m is OsuModEasy))
|
||||||
|
ar = Math.Max(0, ar / 2);
|
||||||
|
double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450);
|
||||||
|
realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5;
|
||||||
|
|
||||||
|
// Custom multipliers for NoFail and SpunOut.
|
||||||
|
double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
||||||
|
|
||||||
|
if (mods.Any(m => m is OsuModNoFail))
|
||||||
|
multiplier *= 0.90f;
|
||||||
|
|
||||||
|
if (mods.Any(m => m is OsuModSpunOut))
|
||||||
|
multiplier *= 0.95f;
|
||||||
|
|
||||||
|
double aimValue = computeAimValue();
|
||||||
|
double speedValue = computeSpeedValue();
|
||||||
|
double accuracyValue = computeAccuracyValue();
|
||||||
|
double totalValue =
|
||||||
|
Math.Pow(
|
||||||
|
Math.Pow(aimValue, 1.1f) +
|
||||||
|
Math.Pow(speedValue, 1.1f) +
|
||||||
|
Math.Pow(accuracyValue, 1.1f), 1.0f / 1.1f
|
||||||
|
) * multiplier;
|
||||||
|
|
||||||
|
if (categoryRatings != null)
|
||||||
|
{
|
||||||
|
categoryRatings.Add("Aim", aimValue);
|
||||||
|
categoryRatings.Add("Speed", speedValue);
|
||||||
|
categoryRatings.Add("Accuracy", accuracyValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double computeAimValue()
|
||||||
|
{
|
||||||
|
double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Aim"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
|
||||||
|
|
||||||
|
// Longer maps are worth more
|
||||||
|
double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
|
||||||
|
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
|
||||||
|
|
||||||
|
aimValue *= lengthBonus;
|
||||||
|
|
||||||
|
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
||||||
|
aimValue *= Math.Pow(0.97f, countMiss);
|
||||||
|
|
||||||
|
// Combo scaling
|
||||||
|
if (beatmapMaxCombo > 0)
|
||||||
|
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
|
||||||
|
|
||||||
|
double approachRateFactor = 1.0f;
|
||||||
|
if (realApproachRate > 10.33f)
|
||||||
|
approachRateFactor += 0.45f * (realApproachRate - 10.33f);
|
||||||
|
else if (realApproachRate < 8.0f)
|
||||||
|
{
|
||||||
|
// HD is worth more with lower ar!
|
||||||
|
if (mods.Any(h => h is OsuModHidden))
|
||||||
|
approachRateFactor += 0.02f * (8.0f - realApproachRate);
|
||||||
|
else
|
||||||
|
approachRateFactor += 0.01f * (8.0f - realApproachRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
aimValue *= approachRateFactor;
|
||||||
|
|
||||||
|
if (mods.Any(h => h is OsuModHidden))
|
||||||
|
aimValue *= 1.18f;
|
||||||
|
|
||||||
|
if (mods.Any(h => h is OsuModFlashlight))
|
||||||
|
{
|
||||||
|
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
|
||||||
|
aimValue *= 1.45f * lengthBonus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale the aim value with accuracy _slightly_
|
||||||
|
aimValue *= 0.5f + accuracy / 2.0f;
|
||||||
|
// It is important to also consider accuracy difficulty when doing that
|
||||||
|
aimValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500;
|
||||||
|
|
||||||
|
return aimValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double computeSpeedValue()
|
||||||
|
{
|
||||||
|
double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Speed"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
|
||||||
|
|
||||||
|
// Longer maps are worth more
|
||||||
|
speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
|
||||||
|
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
|
||||||
|
|
||||||
|
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
||||||
|
speedValue *= Math.Pow(0.97f, countMiss);
|
||||||
|
|
||||||
|
// Combo scaling
|
||||||
|
if (beatmapMaxCombo > 0)
|
||||||
|
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
|
||||||
|
|
||||||
|
// Scale the speed value with accuracy _slightly_
|
||||||
|
speedValue *= 0.5f + accuracy / 2.0f;
|
||||||
|
// It is important to also consider accuracy difficulty when doing that
|
||||||
|
speedValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500;
|
||||||
|
|
||||||
|
return speedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double computeAccuracyValue()
|
||||||
|
{
|
||||||
|
// This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window
|
||||||
|
double betterAccuracyPercentage;
|
||||||
|
int amountHitObjectsWithAccuracy = countHitCircles;
|
||||||
|
|
||||||
|
if (amountHitObjectsWithAccuracy > 0)
|
||||||
|
betterAccuracyPercentage = ((count300 - (totalHits - amountHitObjectsWithAccuracy)) * 6 + count100 * 2 + count50) / (amountHitObjectsWithAccuracy * 6);
|
||||||
|
else
|
||||||
|
betterAccuracyPercentage = 0;
|
||||||
|
|
||||||
|
// It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points
|
||||||
|
if (betterAccuracyPercentage < 0)
|
||||||
|
betterAccuracyPercentage = 0;
|
||||||
|
|
||||||
|
// Lots of arbitrary values from testing.
|
||||||
|
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
||||||
|
double accuracyValue = Math.Pow(1.52163f, Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f;
|
||||||
|
|
||||||
|
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
||||||
|
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
|
||||||
|
|
||||||
|
if (mods.Any(m => m is OsuModHidden))
|
||||||
|
accuracyValue *= 1.02f;
|
||||||
|
if (mods.Any(m => m is OsuModFlashlight))
|
||||||
|
accuracyValue *= 1.02f;
|
||||||
|
|
||||||
|
return accuracyValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double totalHits => count300 + count100 + count50 + countMiss;
|
||||||
|
private double totalSuccessfulHits => count300 + count100 + count50;
|
||||||
|
|
||||||
|
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
||||||
|
}
|
||||||
|
}
|
16
osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs
Normal file
16
osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
[Ignore("getting CI working")]
|
||||||
|
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||||
|
{
|
||||||
|
public TestCasePerformancePoints()
|
||||||
|
: base(new OsuRuleset(new RulesetInfo()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -85,9 +85,11 @@
|
|||||||
<Compile Include="OsuInputManager.cs" />
|
<Compile Include="OsuInputManager.cs" />
|
||||||
<Compile Include="Replays\OsuReplayInputHandler.cs" />
|
<Compile Include="Replays\OsuReplayInputHandler.cs" />
|
||||||
<Compile Include="Tests\TestCaseHitObjects.cs" />
|
<Compile Include="Tests\TestCaseHitObjects.cs" />
|
||||||
|
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||||
<Compile Include="UI\Cursor\CursorTrail.cs" />
|
<Compile Include="UI\Cursor\CursorTrail.cs" />
|
||||||
<Compile Include="UI\Cursor\GameplayCursor.cs" />
|
<Compile Include="UI\Cursor\GameplayCursor.cs" />
|
||||||
<Compile Include="UI\OsuSettings.cs" />
|
<Compile Include="UI\OsuSettings.cs" />
|
||||||
|
<Compile Include="Scoring\OsuPerformanceCalculator.cs" />
|
||||||
<Compile Include="Scoring\OsuScoreProcessor.cs" />
|
<Compile Include="Scoring\OsuScoreProcessor.cs" />
|
||||||
<Compile Include="UI\OsuRulesetContainer.cs" />
|
<Compile Include="UI\OsuRulesetContainer.cs" />
|
||||||
<Compile Include="UI\OsuPlayfield.cs" />
|
<Compile Include="UI\OsuPlayfield.cs" />
|
||||||
|
@ -5,7 +5,6 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Taiko.Beatmaps;
|
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko
|
namespace osu.Game.Rulesets.Taiko
|
||||||
@ -36,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, string> categoryDifficulty = null)
|
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||||
{
|
{
|
||||||
// Fill our custom DifficultyHitObject class, that carries additional information
|
// Fill our custom DifficultyHitObject class, that carries additional information
|
||||||
difficultyHitObjects.Clear();
|
difficultyHitObjects.Clear();
|
||||||
@ -53,8 +52,8 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
|
|
||||||
if (categoryDifficulty != null)
|
if (categoryDifficulty != null)
|
||||||
{
|
{
|
||||||
categoryDifficulty.Add("Strain", starRating.ToString("0.00", CultureInfo.InvariantCulture));
|
categoryDifficulty.Add("Strain", starRating);
|
||||||
categoryDifficulty.Add("Hit window 300", (35 /*HitObjectManager.HitWindow300*/ / TimeRate).ToString("0.00", CultureInfo.InvariantCulture));
|
categoryDifficulty.Add("Hit window 300", 35 /*HitObjectManager.HitWindow300*/ / TimeRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
return starRating;
|
return starRating;
|
||||||
|
16
osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs
Normal file
16
osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
|
{
|
||||||
|
[Ignore("getting CI working")]
|
||||||
|
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||||
|
{
|
||||||
|
public TestCasePerformancePoints()
|
||||||
|
: base(new TaikoRuleset(new RulesetInfo()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -83,6 +83,7 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Scoring\TaikoScoreProcessor.cs" />
|
<Compile Include="Scoring\TaikoScoreProcessor.cs" />
|
||||||
<Compile Include="TaikoInputManager.cs" />
|
<Compile Include="TaikoInputManager.cs" />
|
||||||
|
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||||
<Compile Include="Tests\TestCaseTaikoPlayfield.cs" />
|
<Compile Include="Tests\TestCaseTaikoPlayfield.cs" />
|
||||||
<Compile Include="UI\HitTarget.cs" />
|
<Compile Include="UI\HitTarget.cs" />
|
||||||
<Compile Include="UI\InputDrum.cs" />
|
<Compile Include="UI\InputDrum.cs" />
|
||||||
|
33
osu.Game.Tests/Visual/TestCaseButtonSystem.cs
Normal file
33
osu.Game.Tests/Visual/TestCaseButtonSystem.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Screens.Menu;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual
|
||||||
|
{
|
||||||
|
internal class TestCaseButtonSystem : OsuTestCase
|
||||||
|
{
|
||||||
|
public TestCaseButtonSystem()
|
||||||
|
{
|
||||||
|
OsuLogo logo;
|
||||||
|
ButtonSystem buttons;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke),
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
buttons = new ButtonSystem(),
|
||||||
|
logo = new OsuLogo()
|
||||||
|
};
|
||||||
|
|
||||||
|
buttons.SetOsuLogo(logo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +0,0 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using System.ComponentModel;
|
|
||||||
using osu.Framework.Graphics.Colour;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Screens.Menu;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual
|
|
||||||
{
|
|
||||||
[Description("main menu")]
|
|
||||||
internal class TestCaseMenuButtonSystem : OsuTestCase
|
|
||||||
{
|
|
||||||
public TestCaseMenuButtonSystem()
|
|
||||||
{
|
|
||||||
Add(new Box
|
|
||||||
{
|
|
||||||
Colour = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke),
|
|
||||||
RelativeSizeAxes = Framework.Graphics.Axes.Both,
|
|
||||||
});
|
|
||||||
Add(new ButtonSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
Add(container = new ExampleContainer());
|
Add(container = new ExampleContainer());
|
||||||
|
|
||||||
AddStep(@"Add button", () => container.Add(new OsuButton
|
AddStep(@"Add button", () => container.Add(new TriangleButton
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Text = @"Button",
|
Text = @"Button",
|
||||||
|
@ -116,7 +116,7 @@
|
|||||||
<Compile Include="Visual\TestCaseKeyCounter.cs" />
|
<Compile Include="Visual\TestCaseKeyCounter.cs" />
|
||||||
<Compile Include="Visual\TestCaseLeaderboard.cs" />
|
<Compile Include="Visual\TestCaseLeaderboard.cs" />
|
||||||
<Compile Include="Visual\TestCaseMedalOverlay.cs" />
|
<Compile Include="Visual\TestCaseMedalOverlay.cs" />
|
||||||
<Compile Include="Visual\TestCaseMenuButtonSystem.cs" />
|
<Compile Include="Visual\TestCaseButtonSystem.cs" />
|
||||||
<Compile Include="Visual\TestCaseMenuOverlays.cs" />
|
<Compile Include="Visual\TestCaseMenuOverlays.cs" />
|
||||||
<Compile Include="Visual\TestCaseMods.cs" />
|
<Compile Include="Visual\TestCaseMods.cs" />
|
||||||
<Compile Include="Visual\TestCaseMusicController.cs" />
|
<Compile Include="Visual\TestCaseMusicController.cs" />
|
||||||
|
@ -80,6 +80,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs the conversion of a hit object.
|
/// Performs the conversion of a hit object.
|
||||||
|
/// This method is generally executed sequentially for all objects in a beatmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="original">The hit object to convert.</param>
|
/// <param name="original">The hit object to convert.</param>
|
||||||
/// <param name="beatmap">The un-converted Beatmap.</param>
|
/// <param name="beatmap">The un-converted Beatmap.</param>
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps
|
|||||||
public class BeatmapMetrics
|
public class BeatmapMetrics
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total vote counts of user ratings on a scale of 0..length.
|
/// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<int> Ratings { get; set; }
|
public IEnumerable<int> Ratings { get; set; }
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
protected double TimeRate = 1;
|
protected double TimeRate = 1;
|
||||||
|
|
||||||
public abstract double Calculate(Dictionary<string, string> categoryDifficulty = null);
|
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject
|
public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject
|
||||||
|
@ -3,12 +3,18 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables
|
namespace osu.Game.Beatmaps.Drawables
|
||||||
{
|
{
|
||||||
@ -22,6 +28,10 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
|
|
||||||
private readonly Container nestedContainer;
|
private readonly Container nestedContainer;
|
||||||
|
|
||||||
|
private readonly Container borderContainer;
|
||||||
|
|
||||||
|
private readonly Box hoverLayer;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => nestedContainer;
|
protected override Container<Drawable> Content => nestedContainer;
|
||||||
|
|
||||||
protected Panel()
|
protected Panel()
|
||||||
@ -29,20 +39,56 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
Height = MAX_HEIGHT;
|
Height = MAX_HEIGHT;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
AddInternal(nestedContainer = new Container
|
AddInternal(borderContainer = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
CornerRadius = 10,
|
CornerRadius = 10,
|
||||||
BorderColour = new Color4(221, 255, 255, 255),
|
BorderColour = new Color4(221, 255, 255, 255),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
nestedContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
hoverLayer = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
Blending = BlendingMode.Additive,
|
||||||
|
},
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SampleChannel sampleHover;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(AudioManager audio, OsuColour colours)
|
||||||
|
{
|
||||||
|
sampleHover = audio.Sample.Get($@"SongSelect/song-ping-variation-{RNG.Next(1, 5)}");
|
||||||
|
hoverLayer.Colour = colours.Blue.Opacity(0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(InputState state)
|
||||||
|
{
|
||||||
|
sampleHover?.Play();
|
||||||
|
|
||||||
|
hoverLayer.FadeIn(100, Easing.OutQuint);
|
||||||
|
return base.OnHover(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(InputState state)
|
||||||
|
{
|
||||||
|
hoverLayer.FadeOut(1000, Easing.OutQuint);
|
||||||
|
base.OnHoverLost(state);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetMultiplicativeAlpha(float alpha)
|
public void SetMultiplicativeAlpha(float alpha)
|
||||||
{
|
{
|
||||||
nestedContainer.Alpha = alpha;
|
borderContainer.Alpha = alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -94,8 +140,8 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
|
|
||||||
protected virtual void Selected()
|
protected virtual void Selected()
|
||||||
{
|
{
|
||||||
nestedContainer.BorderThickness = 2.5f;
|
borderContainer.BorderThickness = 2.5f;
|
||||||
nestedContainer.EdgeEffect = new EdgeEffectParameters
|
borderContainer.EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Glow,
|
Type = EdgeEffectType.Glow,
|
||||||
Colour = new Color4(130, 204, 255, 150),
|
Colour = new Color4(130, 204, 255, 150),
|
||||||
@ -106,8 +152,8 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
|
|
||||||
protected virtual void Deselected()
|
protected virtual void Deselected()
|
||||||
{
|
{
|
||||||
nestedContainer.BorderThickness = 0;
|
borderContainer.BorderThickness = 0;
|
||||||
nestedContainer.EdgeEffect = new EdgeEffectParameters
|
borderContainer.EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Shadow,
|
Type = EdgeEffectType.Shadow,
|
||||||
Offset = new Vector2(1),
|
Offset = new Vector2(1),
|
||||||
|
@ -16,12 +16,13 @@ namespace osu.Game.Configuration
|
|||||||
Set(OsuSetting.Ruleset, 0, 0, int.MaxValue);
|
Set(OsuSetting.Ruleset, 0, 0, int.MaxValue);
|
||||||
Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details);
|
Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details);
|
||||||
|
|
||||||
|
Set(OsuSetting.ShowConvertedBeatmaps, true);
|
||||||
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
|
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
|
||||||
Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1);
|
Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1);
|
||||||
|
|
||||||
Set(OsuSetting.SelectionRandomType, SelectionRandomType.RandomPermutation);
|
Set(OsuSetting.SelectionRandomType, SelectionRandomType.RandomPermutation);
|
||||||
|
|
||||||
Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1, 0.01);
|
Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1);
|
||||||
|
|
||||||
// Online settings
|
// Online settings
|
||||||
Set(OsuSetting.Username, string.Empty);
|
Set(OsuSetting.Username, string.Empty);
|
||||||
@ -112,6 +113,7 @@ namespace osu.Game.Configuration
|
|||||||
SnakingOutSliders,
|
SnakingOutSliders,
|
||||||
ShowFpsDisplay,
|
ShowFpsDisplay,
|
||||||
ChatDisplayHeight,
|
ChatDisplayHeight,
|
||||||
Version
|
Version,
|
||||||
|
ShowConvertedBeatmaps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
var id = obj.ID;
|
var id = obj.ID;
|
||||||
obj = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find<T>(id);
|
obj = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find<T>(id);
|
||||||
|
context.Entry(obj).Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -2,34 +2,39 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Audio.Sample;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Containers
|
namespace osu.Game.Graphics.Containers
|
||||||
{
|
{
|
||||||
public class OsuClickableContainer : ClickableContainer
|
public class OsuClickableContainer : ClickableContainer
|
||||||
{
|
{
|
||||||
protected SampleChannel SampleClick, SampleHover;
|
private readonly HoverSampleSet sampleSet;
|
||||||
|
|
||||||
|
private readonly Container content = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
public OsuClickableContainer(HoverSampleSet sampleSet = HoverSampleSet.Normal)
|
||||||
|
{
|
||||||
|
this.sampleSet = sampleSet;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load()
|
||||||
{
|
{
|
||||||
SampleHover = audio.Sample.Get(@"UI/generic-hover");
|
if (AutoSizeAxes != Axes.None)
|
||||||
SampleClick = audio.Sample.Get(@"UI/generic-click");
|
{
|
||||||
}
|
content.RelativeSizeAxes = RelativeSizeAxes;
|
||||||
|
content.AutoSizeAxes = AutoSizeAxes;
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnHover(InputState state)
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
SampleHover?.Play();
|
content,
|
||||||
return base.OnHover(state);
|
new HoverClickSounds(sampleSet)
|
||||||
}
|
};
|
||||||
|
|
||||||
protected override bool OnClick(InputState state)
|
|
||||||
{
|
|
||||||
SampleClick?.Play();
|
|
||||||
return base.OnClick(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ namespace osu.Game.Graphics.Containers
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
samplePopIn = audio.Sample.Get(@"UI/melodic-5");
|
samplePopIn = audio.Sample.Get(@"UI/overlay-pop-in");
|
||||||
samplePopOut = audio.Sample.Get(@"UI/melodic-4");
|
samplePopOut = audio.Sample.Get(@"UI/overlay-pop-out");
|
||||||
|
|
||||||
StateChanged += onStateChanged;
|
StateChanged += onStateChanged;
|
||||||
}
|
}
|
||||||
|
@ -57,19 +57,31 @@ namespace osu.Game.Graphics
|
|||||||
private void load(FontStore store)
|
private void load(FontStore store)
|
||||||
{
|
{
|
||||||
this.store = store;
|
this.store = store;
|
||||||
|
|
||||||
updateTexture();
|
updateTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
updateTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
private FontAwesome loadedIcon;
|
||||||
private void updateTexture()
|
private void updateTexture()
|
||||||
{
|
{
|
||||||
var texture = store?.Get(((char)icon).ToString());
|
var loadableIcon = icon;
|
||||||
|
|
||||||
|
if (loadableIcon == loadedIcon) return;
|
||||||
|
|
||||||
|
var texture = store?.Get(((char)loadableIcon).ToString());
|
||||||
|
|
||||||
spriteMain.Texture = texture;
|
spriteMain.Texture = texture;
|
||||||
spriteShadow.Texture = texture;
|
spriteShadow.Texture = texture;
|
||||||
|
|
||||||
if (Size == Vector2.Zero)
|
if (Size == Vector2.Zero)
|
||||||
Size = new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0);
|
Size = new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0);
|
||||||
|
|
||||||
|
loadedIcon = loadableIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
||||||
|
@ -20,6 +20,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private const Easing easing = Easing.InOutCubic;
|
private const Easing easing = Easing.InOutCubic;
|
||||||
|
|
||||||
private float length;
|
private float length;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Length of the bar, ranges from 0 to 1
|
/// Length of the bar, ranges from 0 to 1
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -134,4 +135,4 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Vertical = TopToBottom | BottomToTop,
|
Vertical = TopToBottom | BottomToTop,
|
||||||
Horizontal = LeftToRight | RightToLeft,
|
Horizontal = LeftToRight | RightToLeft,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
osu.Game/Graphics/UserInterface/HoverClickSounds.cs
Normal file
36
osu.Game/Graphics/UserInterface/HoverClickSounds.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds hover and click sounds to a drawable.
|
||||||
|
/// Does not draw anything.
|
||||||
|
/// </summary>
|
||||||
|
public class HoverClickSounds : HoverSounds
|
||||||
|
{
|
||||||
|
private SampleChannel sampleClick;
|
||||||
|
|
||||||
|
public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) : base(sampleSet)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(InputState state)
|
||||||
|
{
|
||||||
|
sampleClick?.Play();
|
||||||
|
return base.OnClick(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(AudioManager audio)
|
||||||
|
{
|
||||||
|
sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
osu.Game/Graphics/UserInterface/HoverSounds.cs
Normal file
53
osu.Game/Graphics/UserInterface/HoverSounds.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds hover sounds to a drawable.
|
||||||
|
/// Does not draw anything.
|
||||||
|
/// </summary>
|
||||||
|
public class HoverSounds : CompositeDrawable
|
||||||
|
{
|
||||||
|
private SampleChannel sampleHover;
|
||||||
|
|
||||||
|
protected readonly HoverSampleSet SampleSet;
|
||||||
|
|
||||||
|
public HoverSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal)
|
||||||
|
{
|
||||||
|
SampleSet = sampleSet;
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(InputState state)
|
||||||
|
{
|
||||||
|
sampleHover?.Play();
|
||||||
|
return base.OnHover(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(AudioManager audio)
|
||||||
|
{
|
||||||
|
sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum HoverSampleSet
|
||||||
|
{
|
||||||
|
[Description("")]
|
||||||
|
Loud,
|
||||||
|
[Description("-soft")]
|
||||||
|
Normal,
|
||||||
|
[Description("-softer")]
|
||||||
|
Soft
|
||||||
|
}
|
||||||
|
}
|
@ -1,126 +1,18 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Audio;
|
|
||||||
using osu.Framework.Audio.Sample;
|
|
||||||
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.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Game.Graphics.Backgrounds;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class OsuButton : Button, IFilterable
|
/// <summary>
|
||||||
|
/// A button with added default sound effects.
|
||||||
|
/// </summary>
|
||||||
|
public class OsuButton : Button
|
||||||
{
|
{
|
||||||
private Box hover;
|
|
||||||
|
|
||||||
private SampleChannel sampleClick;
|
|
||||||
private SampleChannel sampleHover;
|
|
||||||
|
|
||||||
protected Triangles Triangles;
|
|
||||||
|
|
||||||
public OsuButton()
|
public OsuButton()
|
||||||
{
|
{
|
||||||
Height = 40;
|
Add(new HoverClickSounds(HoverSampleSet.Loud));
|
||||||
}
|
|
||||||
|
|
||||||
protected override SpriteText CreateText() => new OsuSpriteText
|
|
||||||
{
|
|
||||||
Depth = -1,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Font = @"Exo2.0-Bold",
|
|
||||||
};
|
|
||||||
|
|
||||||
public override bool HandleInput => Action != null;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours, AudioManager audio)
|
|
||||||
{
|
|
||||||
if (Action == null)
|
|
||||||
Colour = OsuColour.Gray(0.5f);
|
|
||||||
|
|
||||||
BackgroundColour = colours.BlueDark;
|
|
||||||
|
|
||||||
Content.Masking = true;
|
|
||||||
Content.CornerRadius = 5;
|
|
||||||
|
|
||||||
AddRange(new Drawable[]
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
sampleClick = audio.Sample.Get(@"UI/generic-click");
|
|
||||||
sampleHover = audio.Sample.Get(@"UI/generic-hover");
|
|
||||||
|
|
||||||
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 OnClick(InputState state)
|
|
||||||
{
|
|
||||||
sampleClick?.Play();
|
|
||||||
return base.OnClick(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnHover(InputState state)
|
|
||||||
{
|
|
||||||
sampleHover?.Play();
|
|
||||||
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 };
|
|
||||||
|
|
||||||
public bool MatchingFilter
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
this.FadeTo(value ? 1 : 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Margin = new MarginPadding { Right = 5 },
|
Margin = new MarginPadding { Right = 5 },
|
||||||
}
|
},
|
||||||
|
new HoverClickSounds()
|
||||||
};
|
};
|
||||||
|
|
||||||
Nub.Current.BindTo(Current);
|
Nub.Current.BindTo(Current);
|
||||||
|
@ -33,7 +33,6 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
if (accentColour == default(Color4))
|
if (accentColour == default(Color4))
|
||||||
accentColour = colours.PinkDarker;
|
accentColour = colours.PinkDarker;
|
||||||
updateAccentColour();
|
updateAccentColour();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateAccentColour()
|
private void updateAccentColour()
|
||||||
@ -137,6 +136,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
nonAccentHoverColour = colours.PinkDarker;
|
nonAccentHoverColour = colours.PinkDarker;
|
||||||
nonAccentSelectedColour = Color4.Black.Opacity(0.5f);
|
nonAccentSelectedColour = Color4.Black.Opacity(0.5f);
|
||||||
updateColours();
|
updateColours();
|
||||||
|
|
||||||
|
AddInternal(new HoverClickSounds(HoverSampleSet.Soft));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateForegroundColour()
|
protected override void UpdateForegroundColour()
|
||||||
@ -183,7 +184,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,8 +238,10 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Margin = new MarginPadding { Right = 4 },
|
Margin = new MarginPadding { Right = 4 },
|
||||||
Size = new Vector2(20),
|
Size = new Vector2(20),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AddInternal(new HoverClickSounds());
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -72,7 +72,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
sampleHover = audio.Sample.Get(@"UI/generic-hover");
|
sampleHover = audio.Sample.Get(@"UI/generic-hover");
|
||||||
sampleClick = audio.Sample.Get(@"UI/generic-click");
|
sampleClick = audio.Sample.Get(@"UI/generic-select");
|
||||||
|
|
||||||
BackgroundColour = Color4.Transparent;
|
BackgroundColour = Color4.Transparent;
|
||||||
BackgroundColourHover = OsuColour.FromHex(@"172023");
|
BackgroundColourHover = OsuColour.FromHex(@"172023");
|
||||||
|
@ -88,7 +88,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
Expanded = true,
|
Expanded = true,
|
||||||
}
|
},
|
||||||
|
new HoverClickSounds()
|
||||||
};
|
};
|
||||||
|
|
||||||
Current.DisabledChanged += disabled =>
|
Current.DisabledChanged += disabled =>
|
||||||
|
@ -131,7 +131,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Colour = Color4.White,
|
Colour = Color4.White,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
}
|
},
|
||||||
|
new HoverClickSounds()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
},
|
},
|
||||||
|
new HoverClickSounds()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
108
osu.Game/Graphics/UserInterface/TriangleButton.cs
Normal file
108
osu.Game/Graphics/UserInterface/TriangleButton.cs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using 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;
|
||||||
|
using osu.Game.Graphics.Backgrounds;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A button with moving triangles in the background.
|
||||||
|
/// </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",
|
||||||
|
};
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
BackgroundColour = colours.BlueDark;
|
||||||
|
|
||||||
|
Content.Masking = true;
|
||||||
|
Content.CornerRadius = 5;
|
||||||
|
|
||||||
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
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 };
|
||||||
|
|
||||||
|
public bool MatchingFilter
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.FadeTo(value ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Development;
|
using osu.Framework.Development;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -137,6 +138,10 @@ namespace osu.Game
|
|||||||
Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap);
|
Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap);
|
||||||
BeatmapManager.DefaultBeatmap = defaultBeatmap;
|
BeatmapManager.DefaultBeatmap = defaultBeatmap;
|
||||||
|
|
||||||
|
// tracks play so loud our samples can't keep up.
|
||||||
|
// this adds a global reduction of track volume for the time being.
|
||||||
|
Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
|
||||||
|
|
||||||
Beatmap.ValueChanged += b =>
|
Beatmap.ValueChanged += b =>
|
||||||
{
|
{
|
||||||
var trackLoaded = lastBeatmap?.TrackLoaded ?? false;
|
var trackLoaded = lastBeatmap?.TrackLoaded ?? false;
|
||||||
|
@ -234,7 +234,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
|
|
||||||
private void handleBeatmapAdd(BeatmapSetInfo beatmap)
|
private void handleBeatmapAdd(BeatmapSetInfo beatmap)
|
||||||
{
|
{
|
||||||
if (beatmap.OnlineBeatmapSetID == BeatmapSet.OnlineBeatmapSetID)
|
if (beatmap.OnlineBeatmapSetID == BeatmapSet?.OnlineBeatmapSetID)
|
||||||
downloadButtonsContainer.FadeOut(transition_duration);
|
downloadButtonsContainer.FadeOut(transition_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ namespace osu.Game.Overlays.Chat
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MessageSender : ClickableContainer, IHasContextMenu
|
private class MessageSender : OsuClickableContainer, IHasContextMenu
|
||||||
{
|
{
|
||||||
private readonly User sender;
|
private readonly User sender;
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ using OpenTK;
|
|||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using System;
|
using System;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Chat
|
namespace osu.Game.Overlays.Chat
|
||||||
{
|
{
|
||||||
@ -259,7 +260,7 @@ namespace osu.Game.Overlays.Chat
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CloseButton : ClickableContainer
|
public class CloseButton : OsuClickableContainer
|
||||||
{
|
{
|
||||||
private readonly SpriteIcon icon;
|
private readonly SpriteIcon icon;
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
if (beatmapSets?.Equals(value) ?? false) return;
|
if (beatmapSets?.Equals(value) ?? false) return;
|
||||||
|
|
||||||
beatmapSets = value;
|
beatmapSets = value?.ToList();
|
||||||
|
|
||||||
if (beatmapSets == null) return;
|
if (beatmapSets == null) return;
|
||||||
|
|
||||||
@ -65,8 +65,6 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
|
|
||||||
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
|
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
|
||||||
|
|
||||||
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +220,11 @@ namespace osu.Game.Overlays
|
|||||||
switch (displayStyle)
|
switch (displayStyle)
|
||||||
{
|
{
|
||||||
case PanelDisplayStyle.Grid:
|
case PanelDisplayStyle.Grid:
|
||||||
return new DirectGridPanel(b);
|
return new DirectGridPanel(b)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return new DirectListPanel(b);
|
return new DirectListPanel(b);
|
||||||
}
|
}
|
||||||
@ -282,7 +284,11 @@ namespace osu.Game.Overlays
|
|||||||
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList();
|
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList();
|
||||||
|
|
||||||
// may not need scheduling; loads async internally.
|
// may not need scheduling; loads async internally.
|
||||||
Schedule(() => BeatmapSets = sets);
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
BeatmapSets = sets;
|
||||||
|
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Overlays.KeyBinding
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ResetButton : OsuButton
|
public class ResetButton : TriangleButton
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
|
@ -17,6 +17,7 @@ using osu.Game.Rulesets.UI;
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Mods
|
namespace osu.Game.Overlays.Mods
|
||||||
{
|
{
|
||||||
@ -148,7 +149,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
// the mods from Mod, only multiple if Mod is a MultiMod
|
// the mods from Mod, only multiple if Mod is a MultiMod
|
||||||
|
|
||||||
public override Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex);
|
public virtual Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex);
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
@ -253,6 +254,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
TextSize = 18,
|
TextSize = 18,
|
||||||
},
|
},
|
||||||
|
new HoverClickSounds()
|
||||||
};
|
};
|
||||||
|
|
||||||
Mod = mod;
|
Mod = mod;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Mods
|
namespace osu.Game.Overlays.Mods
|
||||||
{
|
{
|
||||||
@ -12,8 +11,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ModButtonEmpty : Container
|
public class ModButtonEmpty : Container
|
||||||
{
|
{
|
||||||
public virtual Mod SelectedMod => null;
|
|
||||||
|
|
||||||
public ModButtonEmpty()
|
public ModButtonEmpty()
|
||||||
{
|
{
|
||||||
Size = new Vector2(100f);
|
Size = new Vector2(100f);
|
||||||
|
@ -17,6 +17,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = "Show converted beatmaps",
|
||||||
|
Bindable = config.GetBindable<bool>(OsuSetting.ShowConvertedBeatmaps),
|
||||||
|
},
|
||||||
new SettingsSlider<double, StarSlider>
|
new SettingsSlider<double, StarSlider>
|
||||||
{
|
{
|
||||||
LabelText = "Display beatmaps from",
|
LabelText = "Display beatmaps from",
|
||||||
@ -33,7 +38,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
{
|
{
|
||||||
LabelText = "Random beatmap selection",
|
LabelText = "Random beatmap selection",
|
||||||
Bindable = config.GetBindable<SelectionRandomType>(OsuSetting.SelectionRandomType),
|
Bindable = config.GetBindable<SelectionRandomType>(OsuSetting.SelectionRandomType),
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
{
|
{
|
||||||
public class GeneralSettings : SettingsSubsection
|
public class GeneralSettings : SettingsSubsection
|
||||||
{
|
{
|
||||||
private OsuButton importButton;
|
private TriangleButton importButton;
|
||||||
private OsuButton deleteButton;
|
private TriangleButton deleteButton;
|
||||||
private OsuButton restoreButton;
|
private TriangleButton restoreButton;
|
||||||
|
|
||||||
protected override string Header => "General";
|
protected override string Header => "General";
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public class SettingsButton : OsuButton
|
public class SettingsButton : TriangleButton
|
||||||
{
|
{
|
||||||
public SettingsButton()
|
public SettingsButton()
|
||||||
{
|
{
|
||||||
|
@ -12,14 +12,14 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public class SidebarButton : Container
|
public class SidebarButton : OsuButton
|
||||||
{
|
{
|
||||||
private readonly SpriteIcon drawableIcon;
|
private readonly SpriteIcon drawableIcon;
|
||||||
private readonly SpriteText headerText;
|
private readonly SpriteText headerText;
|
||||||
private readonly Box backgroundBox;
|
|
||||||
private readonly Box selectionIndicator;
|
private readonly Box selectionIndicator;
|
||||||
private readonly Container text;
|
private readonly Container text;
|
||||||
public Action<SettingsSection> Action;
|
public Action<SettingsSection> Action;
|
||||||
@ -61,17 +61,14 @@ namespace osu.Game.Overlays.Settings
|
|||||||
|
|
||||||
public SidebarButton()
|
public SidebarButton()
|
||||||
{
|
{
|
||||||
|
BackgroundColour = OsuColour.Gray(60);
|
||||||
|
Background.Alpha = 0;
|
||||||
|
|
||||||
Height = Sidebar.DEFAULT_WIDTH;
|
Height = Sidebar.DEFAULT_WIDTH;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Children = new Drawable[]
|
|
||||||
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
backgroundBox = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Blending = BlendingMode.Additive,
|
|
||||||
Colour = OsuColour.Gray(60),
|
|
||||||
Alpha = 0,
|
|
||||||
},
|
|
||||||
text = new Container
|
text = new Container
|
||||||
{
|
{
|
||||||
Width = Sidebar.DEFAULT_WIDTH,
|
Width = Sidebar.DEFAULT_WIDTH,
|
||||||
@ -101,7 +98,7 @@ namespace osu.Game.Overlays.Settings
|
|||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -113,20 +110,19 @@ namespace osu.Game.Overlays.Settings
|
|||||||
protected override bool OnClick(InputState state)
|
protected override bool OnClick(InputState state)
|
||||||
{
|
{
|
||||||
Action?.Invoke(section);
|
Action?.Invoke(section);
|
||||||
backgroundBox.FlashColour(Color4.White, 400);
|
return base.OnClick(state);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(InputState state)
|
protected override bool OnHover(InputState state)
|
||||||
{
|
{
|
||||||
backgroundBox.FadeTo(0.4f, 200);
|
Background.FadeTo(0.4f, 200);
|
||||||
return base.OnHover(state);
|
return base.OnHover(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnHoverLost(InputState state)
|
protected override void OnHoverLost(InputState state)
|
||||||
{
|
{
|
||||||
backgroundBox.FadeTo(0, 200);
|
Background.FadeTo(0, 200);
|
||||||
base.OnHoverLost(state);
|
base.OnHoverLost(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using OpenTK;
|
|||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Toolbar
|
namespace osu.Game.Overlays.Toolbar
|
||||||
{
|
{
|
||||||
@ -74,7 +75,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
private readonly SpriteText tooltip2;
|
private readonly SpriteText tooltip2;
|
||||||
protected FillFlowContainer Flow;
|
protected FillFlowContainer Flow;
|
||||||
|
|
||||||
public ToolbarButton()
|
public ToolbarButton() : base(HoverSampleSet.Loud)
|
||||||
{
|
{
|
||||||
Width = WIDTH;
|
Width = WIDTH;
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
@ -195,4 +196,4 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
{
|
{
|
||||||
public Vector2 Position => new Vector2(MouseX ?? 0, MouseY ?? 0);
|
public Vector2 Position => new Vector2(MouseX ?? 0, MouseY ?? 0);
|
||||||
|
|
||||||
public bool IsImportant => MouseX.HasValue && MouseY.HasValue && (MouseLeft || MouseRight);
|
public virtual bool IsImportant => MouseX.HasValue && MouseY.HasValue && (MouseLeft || MouseRight);
|
||||||
|
|
||||||
public float? MouseX;
|
public float? MouseX;
|
||||||
public float? MouseY;
|
public float? MouseY;
|
||||||
@ -68,4 +68,4 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}";
|
return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets
|
namespace osu.Game.Rulesets
|
||||||
@ -49,6 +50,8 @@ namespace osu.Game.Rulesets
|
|||||||
|
|
||||||
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null);
|
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null);
|
||||||
|
|
||||||
|
public virtual PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => null;
|
||||||
|
|
||||||
public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle };
|
public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle };
|
||||||
|
|
||||||
public abstract string Description { get; }
|
public abstract string Description { get; }
|
||||||
|
35
osu.Game/Rulesets/Scoring/PerformanceCalculator.cs
Normal file
35
osu.Game/Rulesets/Scoring/PerformanceCalculator.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Scoring
|
||||||
|
{
|
||||||
|
public abstract class PerformanceCalculator
|
||||||
|
{
|
||||||
|
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class PerformanceCalculator<TObject> : PerformanceCalculator
|
||||||
|
where TObject : HitObject
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, double> attributes = new Dictionary<string, double>();
|
||||||
|
protected IDictionary<string, double> Attributes => attributes;
|
||||||
|
|
||||||
|
protected readonly Beatmap<TObject> Beatmap;
|
||||||
|
protected readonly Score Score;
|
||||||
|
|
||||||
|
protected PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score)
|
||||||
|
{
|
||||||
|
Beatmap = CreateBeatmapConverter().Convert(beatmap);
|
||||||
|
Score = score;
|
||||||
|
|
||||||
|
var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods);
|
||||||
|
diffCalc.Calculate(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract BeatmapConverter<TObject> CreateBeatmapConverter();
|
||||||
|
}
|
||||||
|
}
|
@ -174,7 +174,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
sampleHover = audio.Sample.Get(@"Menu/hover");
|
sampleHover = audio.Sample.Get(@"Menu/button-hover");
|
||||||
if (!string.IsNullOrEmpty(sampleName))
|
if (!string.IsNullOrEmpty(sampleName))
|
||||||
sampleClick = audio.Sample.Get($@"Menu/{sampleName}");
|
sampleClick = audio.Sample.Get($@"Menu/{sampleName}");
|
||||||
}
|
}
|
||||||
|
@ -117,13 +117,13 @@ namespace osu.Game.Screens.Menu
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
buttonsPlay.Add(new Button(@"solo", @"select-6", FontAwesome.fa_user, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
|
buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.fa_user, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
|
||||||
buttonsPlay.Add(new Button(@"multi", @"select-5", FontAwesome.fa_users, new Color4(94, 63, 186, 255), () => OnMulti?.Invoke(), 0, Key.M));
|
buttonsPlay.Add(new Button(@"multi", @"button-generic-select", FontAwesome.fa_users, new Color4(94, 63, 186, 255), () => OnMulti?.Invoke(), 0, Key.M));
|
||||||
buttonsPlay.Add(new Button(@"chart", @"select-5", FontAwesome.fa_osu_charts, new Color4(80, 53, 160, 255), () => OnChart?.Invoke()));
|
buttonsPlay.Add(new Button(@"chart", @"button-generic-select", FontAwesome.fa_osu_charts, new Color4(80, 53, 160, 255), () => OnChart?.Invoke()));
|
||||||
|
|
||||||
buttonsTopLevel.Add(new Button(@"play", @"select-1", FontAwesome.fa_osu_logo, new Color4(102, 68, 204, 255), onPlay, WEDGE_WIDTH, Key.P));
|
buttonsTopLevel.Add(new Button(@"play", @"button-play-select", FontAwesome.fa_osu_logo, new Color4(102, 68, 204, 255), onPlay, WEDGE_WIDTH, Key.P));
|
||||||
buttonsTopLevel.Add(new Button(@"osu!editor", @"select-5", FontAwesome.fa_osu_edit_o, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
|
buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", FontAwesome.fa_osu_edit_o, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
|
||||||
buttonsTopLevel.Add(new Button(@"osu!direct", string.Empty, FontAwesome.fa_osu_chevron_down_o, new Color4(165, 204, 0, 255), () => OnDirect?.Invoke(), 0, Key.D));
|
buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", FontAwesome.fa_osu_chevron_down_o, new Color4(165, 204, 0, 255), () => OnDirect?.Invoke(), 0, Key.D));
|
||||||
buttonsTopLevel.Add(new Button(@"exit", string.Empty, FontAwesome.fa_osu_cross_o, new Color4(238, 51, 153, 255), onExit, 0, Key.Q));
|
buttonsTopLevel.Add(new Button(@"exit", string.Empty, FontAwesome.fa_osu_cross_o, new Color4(238, 51, 153, 255), onExit, 0, Key.Q));
|
||||||
|
|
||||||
buttonFlow.AddRange(buttonsPlay);
|
buttonFlow.AddRange(buttonsPlay);
|
||||||
@ -134,7 +134,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
private void load(AudioManager audio, OsuGame game = null)
|
private void load(AudioManager audio, OsuGame game = null)
|
||||||
{
|
{
|
||||||
toolbar = game?.Toolbar;
|
toolbar = game?.Toolbar;
|
||||||
sampleBack = audio.Sample.Get(@"Menu/select-4");
|
sampleBack = audio.Sample.Get(@"Menu/button-back-select");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||||
@ -180,19 +180,21 @@ namespace osu.Game.Screens.Menu
|
|||||||
State = MenuState.TopLevel;
|
State = MenuState.TopLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onOsuLogo()
|
private bool onOsuLogo()
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
case MenuState.Initial:
|
case MenuState.Initial:
|
||||||
State = MenuState.TopLevel;
|
State = MenuState.TopLevel;
|
||||||
return;
|
return true;
|
||||||
case MenuState.TopLevel:
|
case MenuState.TopLevel:
|
||||||
buttonsTopLevel.First().TriggerOnClick();
|
buttonsTopLevel.First().TriggerOnClick();
|
||||||
return;
|
return false;
|
||||||
case MenuState.Play:
|
case MenuState.Play:
|
||||||
buttonsPlay.First().TriggerOnClick();
|
buttonsPlay.First().TriggerOnClick();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,10 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
private readonly Triangles triangles;
|
private readonly Triangles triangles;
|
||||||
|
|
||||||
public Action Action;
|
/// <summary>
|
||||||
|
/// Return value decides whether the logo should play its own sample for the click action.
|
||||||
|
/// </summary>
|
||||||
|
public Func<bool> Action;
|
||||||
|
|
||||||
public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.74f;
|
public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.74f;
|
||||||
|
|
||||||
@ -248,8 +251,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(TextureStore textures, AudioManager audio)
|
private void load(TextureStore textures, AudioManager audio)
|
||||||
{
|
{
|
||||||
sampleClick = audio.Sample.Get(@"Menu/select-2");
|
sampleClick = audio.Sample.Get(@"Menu/osu-logo-select");
|
||||||
sampleBeat = audio.Sample.Get(@"Menu/heartbeat");
|
sampleBeat = audio.Sample.Get(@"Menu/osu-logo-heartbeat");
|
||||||
|
|
||||||
logo.Texture = textures.Get(@"Menu/logo");
|
logo.Texture = textures.Get(@"Menu/logo");
|
||||||
ripple.Texture = textures.Get(@"Menu/logo");
|
ripple.Texture = textures.Get(@"Menu/logo");
|
||||||
@ -354,13 +357,12 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
if (!interactive) return false;
|
if (!interactive) return false;
|
||||||
|
|
||||||
sampleClick.Play();
|
if (Action?.Invoke() ?? true)
|
||||||
|
sampleClick.Play();
|
||||||
|
|
||||||
flashLayer.ClearTransforms();
|
flashLayer.ClearTransforms();
|
||||||
flashLayer.Alpha = 0.4f;
|
flashLayer.Alpha = 0.4f;
|
||||||
flashLayer.FadeOut(1500, Easing.OutExpo);
|
flashLayer.FadeOut(1500, Easing.OutExpo);
|
||||||
|
|
||||||
Action?.Invoke();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ namespace osu.Game.Screens
|
|||||||
if (osuGame != null)
|
if (osuGame != null)
|
||||||
Ruleset.BindTo(osuGame.Ruleset);
|
Ruleset.BindTo(osuGame.Ruleset);
|
||||||
|
|
||||||
sampleExit = audio.Sample.Get(@"UI/melodic-1");
|
sampleExit = audio.Sample.Get(@"UI/screen-back");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnResuming(Screen last)
|
protected override void OnResuming(Screen last)
|
||||||
|
@ -22,8 +22,6 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
public bool IsPaused { get; private set; }
|
public bool IsPaused { get; private set; }
|
||||||
|
|
||||||
public bool AllowExit => IsPaused && pauseOverlay.Alpha == 1;
|
|
||||||
|
|
||||||
public Func<bool> CheckCanPause;
|
public Func<bool> CheckCanPause;
|
||||||
|
|
||||||
private const double pause_cooldown = 1000;
|
private const double pause_cooldown = 1000;
|
||||||
|
@ -46,6 +46,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public bool HasFailed { get; private set; }
|
public bool HasFailed { get; private set; }
|
||||||
|
|
||||||
|
public bool AllowPause { get; set; } = true;
|
||||||
|
|
||||||
public int RestartCount;
|
public int RestartCount;
|
||||||
|
|
||||||
private IAdjustableClock adjustableSourceClock;
|
private IAdjustableClock adjustableSourceClock;
|
||||||
@ -158,7 +160,7 @@ namespace osu.Game.Screens.Play
|
|||||||
FramedClock = offsetClock,
|
FramedClock = offsetClock,
|
||||||
OnRetry = Restart,
|
OnRetry = Restart,
|
||||||
OnQuit = Exit,
|
OnQuit = Exit,
|
||||||
CheckCanPause = () => ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
|
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
|
||||||
Retries = RestartCount,
|
Retries = RestartCount,
|
||||||
OnPause = () => {
|
OnPause = () => {
|
||||||
hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
|
hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
|
||||||
@ -308,7 +310,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (!loadedSuccessfully)
|
if (!loadedSuccessfully)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
(Background as BackgroundScreenBeatmap)?.BlurTo(Vector2.Zero, 1500, Easing.OutQuint);
|
(Background as BackgroundScreenBeatmap)?.BlurTo(Vector2.Zero, 1000, Easing.OutQuint);
|
||||||
|
|
||||||
dimLevel.ValueChanged += dimLevel_ValueChanged;
|
dimLevel.ValueChanged += dimLevel_ValueChanged;
|
||||||
showStoryboard.ValueChanged += showStoryboard_ValueChanged;
|
showStoryboard.ValueChanged += showStoryboard_ValueChanged;
|
||||||
@ -355,7 +357,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
protected override bool OnExiting(Screen next)
|
protected override bool OnExiting(Screen next)
|
||||||
{
|
{
|
||||||
if (HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || RulesetContainer?.HasReplayLoaded != false)
|
if (!AllowPause || HasFailed || !ValidForResume || pauseContainer?.IsPaused != false || RulesetContainer?.HasReplayLoaded != false)
|
||||||
{
|
{
|
||||||
// In the case of replays, we may have changed the playback rate.
|
// In the case of replays, we may have changed the playback rate.
|
||||||
applyRateFromMods();
|
applyRateFromMods();
|
||||||
|
@ -29,11 +29,17 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
if (value == metrics) return;
|
if (value == metrics) return;
|
||||||
metrics = value;
|
metrics = value;
|
||||||
|
|
||||||
var ratings = Metrics.Ratings.ToList();
|
const int rating_range = 10;
|
||||||
negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2 + 1).Sum().ToString();
|
|
||||||
positiveRatings.Text = ratings.GetRange(ratings.Count / 2 + 1, ratings.Count / 2).Sum().ToString();
|
var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0.
|
||||||
ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2 + 1).Sum() / ratings.Sum();
|
|
||||||
graph.Values = Metrics.Ratings.Select(r => (float)r);
|
var negativeCount = ratings.Take(rating_range / 2).Sum();
|
||||||
|
var totalCount = ratings.Sum();
|
||||||
|
|
||||||
|
negativeRatings.Text = negativeCount.ToString();
|
||||||
|
positiveRatings.Text = (totalCount - negativeCount).ToString();
|
||||||
|
ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount;
|
||||||
|
graph.Values = ratings.Take(rating_range).Select(r => (float)r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ using osu.Game.Screens.Select.Filter;
|
|||||||
using Container = osu.Framework.Graphics.Containers.Container;
|
using Container = osu.Framework.Graphics.Containers.Container;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select
|
namespace osu.Game.Screens.Select
|
||||||
@ -60,6 +61,7 @@ namespace osu.Game.Screens.Select
|
|||||||
Group = group,
|
Group = group,
|
||||||
Sort = sort,
|
Sort = sort,
|
||||||
SearchText = searchTextBox.Text,
|
SearchText = searchTextBox.Text,
|
||||||
|
AllowConvertedBeatmaps = showConverted,
|
||||||
Ruleset = ruleset
|
Ruleset = ruleset
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -163,17 +165,24 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||||
|
|
||||||
|
private Bindable<bool> showConverted;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(permitNulls: true)]
|
[BackgroundDependencyLoader(permitNulls: true)]
|
||||||
private void load(OsuColour colours, OsuGame osu)
|
private void load(OsuColour colours, OsuGame osu, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
sortTabs.AccentColour = colours.GreenLight;
|
sortTabs.AccentColour = colours.GreenLight;
|
||||||
|
|
||||||
|
showConverted = config.GetBindable<bool>(OsuSetting.ShowConvertedBeatmaps);
|
||||||
|
showConverted.ValueChanged += val => updateCriteria();
|
||||||
|
|
||||||
if (osu != null)
|
if (osu != null)
|
||||||
ruleset.BindTo(osu.Ruleset);
|
ruleset.BindTo(osu.Ruleset);
|
||||||
ruleset.ValueChanged += val => FilterChanged?.Invoke(CreateCriteria());
|
ruleset.ValueChanged += val => updateCriteria();
|
||||||
ruleset.TriggerChange();
|
ruleset.TriggerChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
|
||||||
|
|
||||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
|
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
|
||||||
|
|
||||||
protected override bool OnMouseMove(InputState state) => true;
|
protected override bool OnMouseMove(InputState state) => true;
|
||||||
@ -182,4 +191,4 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
protected override bool OnDragStart(InputState state) => true;
|
protected override bool OnDragStart(InputState state) => true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ namespace osu.Game.Screens.Select
|
|||||||
public SortMode Sort;
|
public SortMode Sort;
|
||||||
public string SearchText;
|
public string SearchText;
|
||||||
public RulesetInfo Ruleset;
|
public RulesetInfo Ruleset;
|
||||||
|
public bool AllowConvertedBeatmaps;
|
||||||
|
|
||||||
public void Filter(List<BeatmapGroup> groups)
|
public void Filter(List<BeatmapGroup> groups)
|
||||||
{
|
{
|
||||||
@ -23,7 +24,7 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
var set = g.BeatmapSet;
|
var set = g.BeatmapSet;
|
||||||
|
|
||||||
bool hasCurrentMode = set.Beatmaps.Any(bm => bm.RulesetID == (Ruleset?.ID ?? 0));
|
bool hasCurrentMode = AllowConvertedBeatmaps || set.Beatmaps.Any(bm => bm.RulesetID == (Ruleset?.ID ?? 0));
|
||||||
|
|
||||||
bool match = hasCurrentMode;
|
bool match = hasCurrentMode;
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
@ -42,9 +44,13 @@ namespace osu.Game.Screens.Select
|
|||||||
beatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s));
|
beatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SampleChannel sampleConfirm;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours, AudioManager audio)
|
||||||
{
|
{
|
||||||
|
sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection");
|
||||||
|
|
||||||
Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue);
|
Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue);
|
||||||
|
|
||||||
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
|
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
|
||||||
@ -128,6 +134,8 @@ namespace osu.Game.Screens.Select
|
|||||||
Beatmap.Value.Track.Looping = false;
|
Beatmap.Value.Track.Looping = false;
|
||||||
Beatmap.Disabled = true;
|
Beatmap.Disabled = true;
|
||||||
|
|
||||||
|
sampleConfirm?.Play();
|
||||||
|
|
||||||
LoadComponentAsync(player = new PlayerLoader(new Player()), l => Push(player));
|
LoadComponentAsync(player = new PlayerLoader(new Player()), l => Push(player));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,8 +203,8 @@ namespace osu.Game.Screens.Select
|
|||||||
Push(new Editor());
|
Push(new Editor());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onBeatmapRestored(BeatmapInfo b) => carousel.UpdateBeatmap(b);
|
private void onBeatmapRestored(BeatmapInfo b) => Schedule(() => carousel.UpdateBeatmap(b));
|
||||||
private void onBeatmapHidden(BeatmapInfo b) => carousel.UpdateBeatmap(b);
|
private void onBeatmapHidden(BeatmapInfo b) => Schedule(() => carousel.UpdateBeatmap(b));
|
||||||
|
|
||||||
private void carouselBeatmapsLoaded()
|
private void carouselBeatmapsLoaded()
|
||||||
{
|
{
|
||||||
@ -332,7 +332,11 @@ namespace osu.Game.Screens.Select
|
|||||||
logo.FadeIn(logo_transition, Easing.OutQuint);
|
logo.FadeIn(logo_transition, Easing.OutQuint);
|
||||||
logo.ScaleTo(0.4f, logo_transition, Easing.OutQuint);
|
logo.ScaleTo(0.4f, logo_transition, Easing.OutQuint);
|
||||||
|
|
||||||
logo.Action = () => carouselRaisedStart();
|
logo.Action = () =>
|
||||||
|
{
|
||||||
|
carouselRaisedStart();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LogoExiting(OsuLogo logo)
|
protected override void LogoExiting(OsuLogo logo)
|
||||||
@ -413,7 +417,7 @@ namespace osu.Game.Screens.Select
|
|||||||
if (backgroundModeBeatmap != null)
|
if (backgroundModeBeatmap != null)
|
||||||
{
|
{
|
||||||
backgroundModeBeatmap.Beatmap = beatmap;
|
backgroundModeBeatmap.Beatmap = beatmap;
|
||||||
backgroundModeBeatmap.BlurTo(background_blur, 1000);
|
backgroundModeBeatmap.BlurTo(background_blur, 750, Easing.OutQuint);
|
||||||
backgroundModeBeatmap.FadeTo(1, 250);
|
backgroundModeBeatmap.FadeTo(1, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,21 +193,21 @@ namespace osu.Game.Screens.Tournament
|
|||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuButton
|
new TriangleButton
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
|
||||||
Text = "Begin random",
|
Text = "Begin random",
|
||||||
Action = teamsContainer.StartScrolling,
|
Action = teamsContainer.StartScrolling,
|
||||||
},
|
},
|
||||||
new OsuButton
|
new TriangleButton
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
|
||||||
Text = "Stop random",
|
Text = "Stop random",
|
||||||
Action = teamsContainer.StopScrolling,
|
Action = teamsContainer.StopScrolling,
|
||||||
},
|
},
|
||||||
new OsuButton
|
new TriangleButton
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
|
||||||
@ -232,7 +232,7 @@ namespace osu.Game.Screens.Tournament
|
|||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuButton
|
new TriangleButton
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
|
||||||
|
48
osu.Game/Tests/Visual/ScreenTestCase.cs
Normal file
48
osu.Game/Tests/Visual/ScreenTestCase.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Screens;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A test case which can be used to test a screen (that relies on OnEntering being called to execute startup instructions).
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ScreenTestCase : OsuTestCase
|
||||||
|
{
|
||||||
|
private readonly TestOsuScreen baseScreen;
|
||||||
|
|
||||||
|
protected ScreenTestCase()
|
||||||
|
{
|
||||||
|
Add(baseScreen = new TestOsuScreen());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void LoadScreen(OsuScreen screen) => baseScreen.LoadScreen(screen);
|
||||||
|
|
||||||
|
public class TestOsuScreen : OsuScreen
|
||||||
|
{
|
||||||
|
private OsuScreen nextScreen;
|
||||||
|
|
||||||
|
public void LoadScreen(OsuScreen screen) => Schedule(() =>
|
||||||
|
{
|
||||||
|
nextScreen = screen;
|
||||||
|
|
||||||
|
if (IsCurrentScreen)
|
||||||
|
{
|
||||||
|
Push(screen);
|
||||||
|
nextScreen = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
MakeCurrent();
|
||||||
|
});
|
||||||
|
|
||||||
|
protected override void OnResuming(Screen last)
|
||||||
|
{
|
||||||
|
base.OnResuming(last);
|
||||||
|
if (nextScreen != null)
|
||||||
|
LoadScreen(nextScreen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
395
osu.Game/Tests/Visual/TestCasePerformancePoints.cs
Normal file
395
osu.Game/Tests/Visual/TestCasePerformancePoints.cs
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Caching;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual
|
||||||
|
{
|
||||||
|
public abstract class TestCasePerformancePoints : OsuTestCase
|
||||||
|
{
|
||||||
|
protected TestCasePerformancePoints(Ruleset ruleset)
|
||||||
|
{
|
||||||
|
Child = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Black,
|
||||||
|
Alpha = 0.5f,
|
||||||
|
},
|
||||||
|
new ScrollContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new BeatmapList(ruleset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Black,
|
||||||
|
Alpha = 0.5f,
|
||||||
|
},
|
||||||
|
new ScrollContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new StarRatingGrid()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Black,
|
||||||
|
Alpha = 0.5f,
|
||||||
|
},
|
||||||
|
new ScrollContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new PerformanceList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.Absolute, 20),
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.Absolute, 20)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BeatmapList : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly Container<BeatmapDisplay> beatmapDisplays;
|
||||||
|
private readonly Ruleset ruleset;
|
||||||
|
|
||||||
|
public BeatmapList(Ruleset ruleset)
|
||||||
|
{
|
||||||
|
this.ruleset = ruleset;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
InternalChild = beatmapDisplays = new FillFlowContainer<BeatmapDisplay>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 4)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(BeatmapManager beatmaps)
|
||||||
|
{
|
||||||
|
var sets = beatmaps.GetAllUsableBeatmapSets();
|
||||||
|
var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID < 0 || b.RulesetID == ruleset.LegacyID);
|
||||||
|
|
||||||
|
allBeatmaps.ForEach(b => beatmapDisplays.Add(new BeatmapDisplay(b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BeatmapDisplay : CompositeDrawable, IHasTooltip
|
||||||
|
{
|
||||||
|
private readonly OsuSpriteText text;
|
||||||
|
private readonly BeatmapInfo beatmap;
|
||||||
|
|
||||||
|
private BeatmapManager beatmaps;
|
||||||
|
private OsuGameBase osuGame;
|
||||||
|
|
||||||
|
private bool isSelected;
|
||||||
|
|
||||||
|
public string TooltipText => text.Text;
|
||||||
|
|
||||||
|
public BeatmapDisplay(BeatmapInfo beatmap)
|
||||||
|
{
|
||||||
|
this.beatmap = beatmap;
|
||||||
|
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
InternalChild = text = new OsuSpriteText();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(InputState state)
|
||||||
|
{
|
||||||
|
if (osuGame.Beatmap.Value.BeatmapInfo.ID == beatmap.ID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
osuGame.Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap);
|
||||||
|
isSelected = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(InputState state)
|
||||||
|
{
|
||||||
|
if (isSelected)
|
||||||
|
return false;
|
||||||
|
this.FadeColour(Color4.Yellow, 100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(InputState state)
|
||||||
|
{
|
||||||
|
if (isSelected)
|
||||||
|
return;
|
||||||
|
this.FadeColour(Color4.White, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuGameBase osuGame, BeatmapManager beatmaps)
|
||||||
|
{
|
||||||
|
this.osuGame = osuGame;
|
||||||
|
this.beatmaps = beatmaps;
|
||||||
|
|
||||||
|
var working = beatmaps.GetWorkingBeatmap(beatmap);
|
||||||
|
text.Text = $"{working.Metadata.Artist} - {working.Metadata.Title} ({working.Metadata.AuthorString}) [{working.BeatmapInfo.Version}]";
|
||||||
|
|
||||||
|
osuGame.Beatmap.ValueChanged += beatmapChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beatmapChanged(WorkingBeatmap newBeatmap)
|
||||||
|
{
|
||||||
|
if (isSelected)
|
||||||
|
this.FadeColour(Color4.White, 100);
|
||||||
|
isSelected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PerformanceList : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly FillFlowContainer<PerformanceDisplay> scores;
|
||||||
|
private APIAccess api;
|
||||||
|
|
||||||
|
public PerformanceList()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
InternalChild = scores = new FillFlowContainer<PerformanceDisplay>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 4)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuGameBase osuGame, APIAccess api)
|
||||||
|
{
|
||||||
|
this.api = api;
|
||||||
|
|
||||||
|
if (!api.IsLoggedIn)
|
||||||
|
{
|
||||||
|
InternalChild = new SpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Text = "Please login to see online scores",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
osuGame.Beatmap.ValueChanged += beatmapChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GetScoresRequest lastRequest;
|
||||||
|
private void beatmapChanged(WorkingBeatmap newBeatmap)
|
||||||
|
{
|
||||||
|
lastRequest?.Cancel();
|
||||||
|
scores.Clear();
|
||||||
|
|
||||||
|
if (!api.IsLoggedIn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo);
|
||||||
|
lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new PerformanceDisplay(s, newBeatmap.Beatmap)));
|
||||||
|
api.Queue(lastRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PerformanceDisplay : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly OsuSpriteText text;
|
||||||
|
|
||||||
|
private readonly Score score;
|
||||||
|
private readonly Beatmap beatmap;
|
||||||
|
|
||||||
|
public PerformanceDisplay(Score score, Beatmap beatmap)
|
||||||
|
{
|
||||||
|
this.score = score;
|
||||||
|
this.beatmap = beatmap;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
InternalChild = text = new OsuSpriteText();
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance();
|
||||||
|
var calculator = ruleset.CreatePerformanceCalculator(beatmap, score);
|
||||||
|
if (calculator == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var attributes = new Dictionary<string, double>();
|
||||||
|
double performance = calculator.Calculate(attributes);
|
||||||
|
|
||||||
|
text.Text = $"{score.User.Username} -> online: {score.PP:n2}pp | local: {performance:n2}pp";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StarRatingGrid : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly FillFlowContainer<OsuCheckbox> modFlow;
|
||||||
|
private readonly OsuSpriteText totalText;
|
||||||
|
private readonly FillFlowContainer categoryTexts;
|
||||||
|
|
||||||
|
public StarRatingGrid()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
modFlow = new FillFlowContainer<OsuCheckbox>
|
||||||
|
{
|
||||||
|
Name = "Checkbox flow",
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Spacing = new Vector2(4, 4)
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
Name = "Information display",
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Spacing = new Vector2(0, 4),
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
totalText = new OsuSpriteText { TextSize = 24 },
|
||||||
|
categoryTexts = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuGameBase osuGame)
|
||||||
|
{
|
||||||
|
osuGame.Beatmap.ValueChanged += beatmapChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Cached informationCache = new Cached();
|
||||||
|
|
||||||
|
private Ruleset ruleset;
|
||||||
|
private WorkingBeatmap beatmap;
|
||||||
|
|
||||||
|
private void beatmapChanged(WorkingBeatmap newBeatmap)
|
||||||
|
{
|
||||||
|
beatmap = newBeatmap;
|
||||||
|
|
||||||
|
modFlow.Clear();
|
||||||
|
|
||||||
|
ruleset = newBeatmap.BeatmapInfo.Ruleset.CreateInstance();
|
||||||
|
foreach (var mod in ruleset.GetAllMods())
|
||||||
|
{
|
||||||
|
var checkBox = new OsuCheckbox
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.None,
|
||||||
|
Width = 50,
|
||||||
|
LabelText = mod.ShortenedName
|
||||||
|
};
|
||||||
|
|
||||||
|
checkBox.Current.ValueChanged += v => informationCache.Invalidate();
|
||||||
|
modFlow.Add(checkBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
informationCache.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (ruleset == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!informationCache.IsValid)
|
||||||
|
{
|
||||||
|
totalText.Text = string.Empty;
|
||||||
|
categoryTexts.Clear();
|
||||||
|
|
||||||
|
var allMods = ruleset.GetAllMods().ToList();
|
||||||
|
Mod[] activeMods = modFlow.Where(c => c.Current.Value).Select(c => allMods.First(m => m.ShortenedName == c.LabelText)).ToArray();
|
||||||
|
|
||||||
|
var diffCalc = ruleset.CreateDifficultyCalculator(beatmap.Beatmap, activeMods);
|
||||||
|
if (diffCalc != null)
|
||||||
|
{
|
||||||
|
var categories = new Dictionary<string, double>();
|
||||||
|
double totalSr = diffCalc.Calculate(categories);
|
||||||
|
|
||||||
|
totalText.Text = $"Star rating: {totalSr:n2}";
|
||||||
|
foreach (var kvp in categories)
|
||||||
|
categoryTexts.Add(new OsuSpriteText { Text = $"{kvp.Key}: {kvp.Value:n2}" });
|
||||||
|
}
|
||||||
|
|
||||||
|
informationCache.Validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user