mirror of
https://github.com/ppy/osu.git
synced 2026-05-25 02:21:17 +08:00
Merge branch 'master' into fix-hold-note-input
This commit is contained in:
+1
-1
@@ -54,6 +54,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1219.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1225.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
using DiscordRPC;
|
using DiscordRPC;
|
||||||
using DiscordRPC.Message;
|
using DiscordRPC.Message;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@@ -43,6 +45,10 @@ namespace osu.Desktop
|
|||||||
};
|
};
|
||||||
|
|
||||||
client.OnReady += onReady;
|
client.OnReady += onReady;
|
||||||
|
|
||||||
|
// safety measure for now, until we performance test / improve backoff for failed connections.
|
||||||
|
client.OnConnectionFailed += (_, __) => client.Deinitialize();
|
||||||
|
|
||||||
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);
|
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);
|
||||||
|
|
||||||
(user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u =>
|
(user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u =>
|
||||||
@@ -77,8 +83,8 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
if (status.Value is UserStatusOnline && activity.Value != null)
|
if (status.Value is UserStatusOnline && activity.Value != null)
|
||||||
{
|
{
|
||||||
presence.State = activity.Value.Status;
|
presence.State = truncate(activity.Value.Status);
|
||||||
presence.Details = getDetails(activity.Value);
|
presence.Details = truncate(getDetails(activity.Value));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -96,6 +102,27 @@ namespace osu.Desktop
|
|||||||
client.SetPresence(presence);
|
client.SetPresence(presence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly int ellipsis_length = Encoding.UTF8.GetByteCount(new[] { '…' });
|
||||||
|
|
||||||
|
private string truncate(string str)
|
||||||
|
{
|
||||||
|
if (Encoding.UTF8.GetByteCount(str) <= 128)
|
||||||
|
return str;
|
||||||
|
|
||||||
|
ReadOnlyMemory<char> strMem = str.AsMemory();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
strMem = strMem[..^1];
|
||||||
|
} while (Encoding.UTF8.GetByteCount(strMem.Span) + ellipsis_length > 128);
|
||||||
|
|
||||||
|
return string.Create(strMem.Length + 1, strMem, (span, mem) =>
|
||||||
|
{
|
||||||
|
mem.Span.CopyTo(span);
|
||||||
|
span[^1] = '…';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private string getDetails(UserActivity activity)
|
private string getDetails(UserActivity activity)
|
||||||
{
|
{
|
||||||
switch (activity)
|
switch (activity)
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
{
|
{
|
||||||
public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
|
public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
|
||||||
{
|
{
|
||||||
public CatchBeatmapConverter(IBeatmap beatmap)
|
public CatchBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
|
||||||
: base(beatmap)
|
: base(beatmap, ruleset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,13 +24,16 @@ using System;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch
|
namespace osu.Game.Rulesets.Catch
|
||||||
{
|
{
|
||||||
public class CatchRuleset : Ruleset
|
public class CatchRuleset : Ruleset, ILegacyRuleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableCatchRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableCatchRuleset(this, beatmap, mods);
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new CatchScoreProcessor(beatmap);
|
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new CatchScoreProcessor(beatmap);
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
|
public override HealthProcessor CreateHealthProcessor(IBeatmap beatmap) => new CatchHealthProcessor(beatmap);
|
||||||
|
|
||||||
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap, this);
|
||||||
|
|
||||||
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
|
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
|
||||||
|
|
||||||
public const string SHORT_NAME = "fruits";
|
public const string SHORT_NAME = "fruits";
|
||||||
@@ -106,6 +109,12 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
new CatchModFlashlight(),
|
new CatchModFlashlight(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case ModType.Conversion:
|
||||||
|
return new Mod[]
|
||||||
|
{
|
||||||
|
new CatchModDifficultyAdjust(),
|
||||||
|
};
|
||||||
|
|
||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
return new Mod[]
|
return new Mod[]
|
||||||
{
|
{
|
||||||
@@ -134,7 +143,7 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
|
|
||||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new CatchPerformanceCalculator(this, beatmap, score);
|
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new CatchPerformanceCalculator(this, beatmap, score);
|
||||||
|
|
||||||
public override int? LegacyID => 2;
|
public int LegacyID => 2;
|
||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
|
{
|
||||||
|
public class CatchModDifficultyAdjust : ModDifficultyAdjust
|
||||||
|
{
|
||||||
|
[SettingSource("Fruit Size", "Override a beatmap's set CS.")]
|
||||||
|
public BindableNumber<float> CircleSize { get; } = new BindableFloat
|
||||||
|
{
|
||||||
|
Precision = 0.1f,
|
||||||
|
MinValue = 1,
|
||||||
|
MaxValue = 10,
|
||||||
|
Default = 5,
|
||||||
|
Value = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Approach Rate", "Override a beatmap's set AR.")]
|
||||||
|
public BindableNumber<float> ApproachRate { get; } = new BindableFloat
|
||||||
|
{
|
||||||
|
Precision = 0.1f,
|
||||||
|
MinValue = 1,
|
||||||
|
MaxValue = 10,
|
||||||
|
Default = 5,
|
||||||
|
Value = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void TransferSettings(BeatmapDifficulty difficulty)
|
||||||
|
{
|
||||||
|
base.TransferSettings(difficulty);
|
||||||
|
|
||||||
|
CircleSize.Value = CircleSize.Default = difficulty.CircleSize;
|
||||||
|
ApproachRate.Value = ApproachRate.Default = difficulty.ApproachRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplySettings(BeatmapDifficulty difficulty)
|
||||||
|
{
|
||||||
|
base.ApplySettings(difficulty);
|
||||||
|
|
||||||
|
difficulty.CircleSize = CircleSize.Value;
|
||||||
|
difficulty.ApproachRate = ApproachRate.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Scoring
|
||||||
|
{
|
||||||
|
public class CatchHealthProcessor : HealthProcessor
|
||||||
|
{
|
||||||
|
public CatchHealthProcessor(IBeatmap beatmap)
|
||||||
|
: base(beatmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private float hpDrainRate;
|
||||||
|
|
||||||
|
protected override void ApplyBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
base.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
|
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double HealthAdjustmentFactorFor(JudgementResult result)
|
||||||
|
{
|
||||||
|
switch (result.Type)
|
||||||
|
{
|
||||||
|
case HitResult.Miss:
|
||||||
|
return hpDrainRate;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 10.2 - hpDrainRate; // Award less HP as drain rate is increased
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Scoring
|
namespace osu.Game.Rulesets.Catch.Scoring
|
||||||
@@ -14,27 +13,6 @@ namespace osu.Game.Rulesets.Catch.Scoring
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private float hpDrainRate;
|
|
||||||
|
|
||||||
protected override void ApplyBeatmap(IBeatmap beatmap)
|
|
||||||
{
|
|
||||||
base.ApplyBeatmap(beatmap);
|
|
||||||
|
|
||||||
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override double HealthAdjustmentFactorFor(JudgementResult result)
|
|
||||||
{
|
|
||||||
switch (result.Type)
|
|
||||||
{
|
|
||||||
case HitResult.Miss:
|
|
||||||
return hpDrainRate;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 10.2 - hpDrainRate; // Award less HP as drain rate is increased
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override HitWindows CreateHitWindows() => new CatchHitWindows();
|
public override HitWindows CreateHitWindows() => new CatchHitWindows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
private ManiaBeatmap beatmap;
|
private ManiaBeatmap beatmap;
|
||||||
|
|
||||||
public ManiaBeatmapConverter(IBeatmap beatmap)
|
public ManiaBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
|
||||||
: base(beatmap)
|
: base(beatmap, ruleset)
|
||||||
{
|
{
|
||||||
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(new ManiaRuleset().RulesetInfo);
|
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
|
||||||
|
|
||||||
var roundedCircleSize = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
|
var roundedCircleSize = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
|
||||||
var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
||||||
|
|||||||
@@ -31,13 +31,15 @@ using osu.Game.Scoring;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania
|
namespace osu.Game.Rulesets.Mania
|
||||||
{
|
{
|
||||||
public class ManiaRuleset : Ruleset
|
public class ManiaRuleset : Ruleset, ILegacyRuleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableManiaRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableManiaRuleset(this, beatmap, mods);
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new ManiaScoreProcessor(beatmap);
|
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new ManiaScoreProcessor(beatmap);
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
|
public override HealthProcessor CreateHealthProcessor(IBeatmap beatmap) => new ManiaHealthProcessor(beatmap);
|
||||||
|
|
||||||
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this);
|
||||||
|
|
||||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
|
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
|
||||||
|
|
||||||
@@ -151,6 +153,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
new ManiaModRandom(),
|
new ManiaModRandom(),
|
||||||
new ManiaModDualStages(),
|
new ManiaModDualStages(),
|
||||||
new ManiaModMirror(),
|
new ManiaModMirror(),
|
||||||
|
new ManiaModDifficultyAdjust(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
@@ -178,7 +181,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap);
|
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap);
|
||||||
|
|
||||||
public override int? LegacyID => 3;
|
public int LegacyID => 3;
|
||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public class ManiaModDifficultyAdjust : ModDifficultyAdjust
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
|
public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
|
||||||
{
|
{
|
||||||
// We don't need to fully convert, just create the converter
|
// We don't need to fully convert, just create the converter
|
||||||
var converter = new ManiaBeatmapConverter(beatmap);
|
var converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
|
||||||
|
|
||||||
// NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling
|
// NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling
|
||||||
// elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage.
|
// elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage.
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Scoring
|
||||||
|
{
|
||||||
|
public class ManiaHealthProcessor : HealthProcessor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The hit HP multiplier at OD = 0.
|
||||||
|
/// </summary>
|
||||||
|
private const double hp_multiplier_min = 0.75;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The hit HP multiplier at OD = 0.
|
||||||
|
/// </summary>
|
||||||
|
private const double hp_multiplier_mid = 0.85;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The hit HP multiplier at OD = 0.
|
||||||
|
/// </summary>
|
||||||
|
private const double hp_multiplier_max = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The MISS HP multiplier at OD = 0.
|
||||||
|
/// </summary>
|
||||||
|
private const double hp_multiplier_miss_min = 0.5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The MISS HP multiplier at OD = 5.
|
||||||
|
/// </summary>
|
||||||
|
private const double hp_multiplier_miss_mid = 0.75;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The MISS HP multiplier at OD = 10.
|
||||||
|
/// </summary>
|
||||||
|
private const double hp_multiplier_miss_max = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The MISS HP multiplier. This is multiplied to the miss hp increase.
|
||||||
|
/// </summary>
|
||||||
|
private double hpMissMultiplier = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The HIT HP multiplier. This is multiplied to hit hp increases.
|
||||||
|
/// </summary>
|
||||||
|
private double hpMultiplier = 1;
|
||||||
|
|
||||||
|
public ManiaHealthProcessor(IBeatmap beatmap)
|
||||||
|
: base(beatmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
base.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
|
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
||||||
|
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
|
||||||
|
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double HealthAdjustmentFactorFor(JudgementResult result)
|
||||||
|
=> result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,86 +2,17 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Scoring
|
namespace osu.Game.Rulesets.Mania.Scoring
|
||||||
{
|
{
|
||||||
internal class ManiaScoreProcessor : ScoreProcessor
|
internal class ManiaScoreProcessor : ScoreProcessor
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The hit HP multiplier at OD = 0.
|
|
||||||
/// </summary>
|
|
||||||
private const double hp_multiplier_min = 0.75;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The hit HP multiplier at OD = 0.
|
|
||||||
/// </summary>
|
|
||||||
private const double hp_multiplier_mid = 0.85;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The hit HP multiplier at OD = 0.
|
|
||||||
/// </summary>
|
|
||||||
private const double hp_multiplier_max = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The MISS HP multiplier at OD = 0.
|
|
||||||
/// </summary>
|
|
||||||
private const double hp_multiplier_miss_min = 0.5;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The MISS HP multiplier at OD = 5.
|
|
||||||
/// </summary>
|
|
||||||
private const double hp_multiplier_miss_mid = 0.75;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The MISS HP multiplier at OD = 10.
|
|
||||||
/// </summary>
|
|
||||||
private const double hp_multiplier_miss_max = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The MISS HP multiplier. This is multiplied to the miss hp increase.
|
|
||||||
/// </summary>
|
|
||||||
private double hpMissMultiplier = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The HIT HP multiplier. This is multiplied to hit hp increases.
|
|
||||||
/// </summary>
|
|
||||||
private double hpMultiplier = 1;
|
|
||||||
|
|
||||||
public ManiaScoreProcessor(IBeatmap beatmap)
|
public ManiaScoreProcessor(IBeatmap beatmap)
|
||||||
: base(beatmap)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyBeatmap(IBeatmap beatmap)
|
|
||||||
{
|
|
||||||
base.ApplyBeatmap(beatmap);
|
|
||||||
|
|
||||||
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
|
||||||
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
|
|
||||||
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void SimulateAutoplay(IBeatmap beatmap)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
base.SimulateAutoplay(beatmap);
|
|
||||||
|
|
||||||
if (!HasFailed)
|
|
||||||
break;
|
|
||||||
|
|
||||||
hpMultiplier *= 1.01;
|
|
||||||
hpMissMultiplier *= 0.98;
|
|
||||||
|
|
||||||
Reset(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override double HealthAdjustmentFactorFor(JudgementResult result)
|
|
||||||
=> result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier;
|
|
||||||
|
|
||||||
public override HitWindows CreateHitWindows() => new ManiaHitWindows();
|
public override HitWindows CreateHitWindows() => new ManiaHitWindows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.IO.Stores;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Screens;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneLegacyBeatmapSkin : OsuTestScene
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private AudioManager audio { get; set; }
|
||||||
|
|
||||||
|
[TestCase(true)]
|
||||||
|
[TestCase(false)]
|
||||||
|
public void TestBeatmapComboColours(bool customSkinColoursPresent)
|
||||||
|
{
|
||||||
|
ExposedPlayer player = null;
|
||||||
|
|
||||||
|
AddStep("load coloured beatmap", () => player = loadBeatmap(customSkinColoursPresent, true));
|
||||||
|
AddUntilStep("wait for player", () => player.IsLoaded);
|
||||||
|
|
||||||
|
AddAssert("is beatmap skin colours", () => player.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatmapNoComboColours()
|
||||||
|
{
|
||||||
|
ExposedPlayer player = null;
|
||||||
|
|
||||||
|
AddStep("load no-colour beatmap", () => player = loadBeatmap(false, false));
|
||||||
|
AddUntilStep("wait for player", () => player.IsLoaded);
|
||||||
|
|
||||||
|
AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatmapNoComboColoursSkinOverride()
|
||||||
|
{
|
||||||
|
ExposedPlayer player = null;
|
||||||
|
|
||||||
|
AddStep("load custom-skin colour", () => player = loadBeatmap(true, false));
|
||||||
|
AddUntilStep("wait for player", () => player.IsLoaded);
|
||||||
|
|
||||||
|
AddAssert("is custom user skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExposedPlayer loadBeatmap(bool userHasCustomColours, bool beatmapHasColours)
|
||||||
|
{
|
||||||
|
ExposedPlayer player;
|
||||||
|
|
||||||
|
Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours);
|
||||||
|
Child = new OsuScreenStack(player = new ExposedPlayer(userHasCustomColours)) { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ExposedPlayer : Player
|
||||||
|
{
|
||||||
|
private readonly bool userHasCustomColours;
|
||||||
|
|
||||||
|
public ExposedPlayer(bool userHasCustomColours)
|
||||||
|
: base(false, false)
|
||||||
|
{
|
||||||
|
this.userHasCustomColours = userHasCustomColours;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
{
|
||||||
|
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
dependencies.CacheAs<ISkinSource>(new TestSkin(userHasCustomColours));
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<Color4> UsableComboColours =>
|
||||||
|
GameplayClockContainer.ChildrenOfType<BeatmapSkinProvidingContainer>()
|
||||||
|
.First()
|
||||||
|
.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
|
||||||
|
{
|
||||||
|
private readonly bool hasColours;
|
||||||
|
|
||||||
|
public CustomSkinWorkingBeatmap(AudioManager audio, bool hasColours)
|
||||||
|
: base(new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
BeatmapSet = new BeatmapSetInfo(),
|
||||||
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
},
|
||||||
|
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
|
||||||
|
}, null, null, audio)
|
||||||
|
{
|
||||||
|
this.hasColours = hasColours;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, hasColours);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestBeatmapSkin : LegacyBeatmapSkin
|
||||||
|
{
|
||||||
|
public static Color4[] Colours { get; } =
|
||||||
|
{
|
||||||
|
new Color4(50, 100, 150, 255),
|
||||||
|
new Color4(40, 80, 120, 255),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours)
|
||||||
|
: base(beatmap, new ResourceStore<byte[]>(), null)
|
||||||
|
{
|
||||||
|
if (hasColours)
|
||||||
|
Configuration.AddComboColours(Colours);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSkin : LegacySkin, ISkinSource
|
||||||
|
{
|
||||||
|
public static Color4[] Colours { get; } =
|
||||||
|
{
|
||||||
|
new Color4(150, 100, 50, 255),
|
||||||
|
new Color4(20, 20, 20, 255),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestSkin(bool hasCustomColours)
|
||||||
|
: base(new SkinInfo(), null, null, string.Empty)
|
||||||
|
{
|
||||||
|
if (hasCustomColours)
|
||||||
|
Configuration.AddComboColours(Colours);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action SourceChanged
|
||||||
|
{
|
||||||
|
add { }
|
||||||
|
remove { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
EndTime = 6000,
|
EndTime = 6000,
|
||||||
},
|
},
|
||||||
// placeholder object to avoid hitting the results screen
|
// placeholder object to avoid hitting the results screen
|
||||||
new HitObject
|
new HitCircle
|
||||||
{
|
{
|
||||||
StartTime = 99999,
|
StartTime = 99999,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
{
|
{
|
||||||
public class OsuBeatmapConverter : BeatmapConverter<OsuHitObject>
|
public class OsuBeatmapConverter : BeatmapConverter<OsuHitObject>
|
||||||
{
|
{
|
||||||
public OsuBeatmapConverter(IBeatmap beatmap)
|
public OsuBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
|
||||||
: base(beatmap)
|
: base(beatmap, ruleset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModBlinds : Mod, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToScoreProcessor
|
public class OsuModBlinds : Mod, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToHealthProcessor
|
||||||
{
|
{
|
||||||
public override string Name => "Blinds";
|
public override string Name => "Blinds";
|
||||||
public override string Description => "Play with blinds on your screen.";
|
public override string Description => "Play with blinds on your screen.";
|
||||||
@@ -37,9 +37,9 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield.HitObjectContainer, drawableRuleset.Beatmap));
|
drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield.HitObjectContainer, drawableRuleset.Beatmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
|
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||||
{
|
{
|
||||||
scoreProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); };
|
healthProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); };
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModDeflate : OsuModeObjectScaleTween
|
public class OsuModDeflate : OsuModObjectScaleTween
|
||||||
{
|
{
|
||||||
public override string Name => "Deflate";
|
public override string Name => "Deflate";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
public class OsuModDifficultyAdjust : ModDifficultyAdjust
|
||||||
|
{
|
||||||
|
[SettingSource("Circle Size", "Override a beatmap's set CS.")]
|
||||||
|
public BindableNumber<float> CircleSize { get; } = new BindableFloat
|
||||||
|
{
|
||||||
|
Precision = 0.1f,
|
||||||
|
MinValue = 1,
|
||||||
|
MaxValue = 10,
|
||||||
|
Default = 5,
|
||||||
|
Value = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Approach Rate", "Override a beatmap's set AR.")]
|
||||||
|
public BindableNumber<float> ApproachRate { get; } = new BindableFloat
|
||||||
|
{
|
||||||
|
Precision = 0.1f,
|
||||||
|
MinValue = 1,
|
||||||
|
MaxValue = 10,
|
||||||
|
Default = 5,
|
||||||
|
Value = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void TransferSettings(BeatmapDifficulty difficulty)
|
||||||
|
{
|
||||||
|
base.TransferSettings(difficulty);
|
||||||
|
|
||||||
|
CircleSize.Value = CircleSize.Default = difficulty.CircleSize;
|
||||||
|
ApproachRate.Value = ApproachRate.Default = difficulty.ApproachRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplySettings(BeatmapDifficulty difficulty)
|
||||||
|
{
|
||||||
|
base.ApplySettings(difficulty);
|
||||||
|
|
||||||
|
difficulty.CircleSize = CircleSize.Value;
|
||||||
|
difficulty.ApproachRate = ApproachRate.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
internal class OsuModGrow : OsuModeObjectScaleTween
|
internal class OsuModGrow : OsuModObjectScaleTween
|
||||||
{
|
{
|
||||||
public override string Name => "Grow";
|
public override string Name => "Grow";
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adjusts the size of hit objects during their fade in animation.
|
/// Adjusts the size of hit objects during their fade in animation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class OsuModeObjectScaleTween : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
|
public abstract class OsuModObjectScaleTween : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
|
||||||
{
|
{
|
||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
// todo: this mod should be able to be compatible with hidden with a bit of further implementation.
|
// todo: this mod should be able to be compatible with hidden with a bit of further implementation.
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModeObjectScaleTween), typeof(OsuModHidden), typeof(OsuModTraceable) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModObjectScaleTween), typeof(OsuModHidden), typeof(OsuModTraceable) };
|
||||||
|
|
||||||
private const int rotate_offset = 360;
|
private const int rotate_offset = 360;
|
||||||
private const float rotate_starting_width = 2;
|
private const float rotate_starting_width = 2;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Description => "Put your faith in the approach circles...";
|
public override string Description => "Put your faith in the approach circles...";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModeObjectScaleTween) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween) };
|
||||||
private Bindable<bool> increaseFirstObjectVisibility = new Bindable<bool>();
|
private Bindable<bool> increaseFirstObjectVisibility = new Bindable<bool>();
|
||||||
|
|
||||||
public void ReadFromConfig(OsuConfigManager config)
|
public void ReadFromConfig(OsuConfigManager config)
|
||||||
|
|||||||
@@ -32,13 +32,15 @@ using System;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu
|
namespace osu.Game.Rulesets.Osu
|
||||||
{
|
{
|
||||||
public class OsuRuleset : Ruleset
|
public class OsuRuleset : Ruleset, ILegacyRuleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableOsuRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableOsuRuleset(this, beatmap, mods);
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new OsuScoreProcessor(beatmap);
|
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new OsuScoreProcessor(beatmap);
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
|
public override HealthProcessor CreateHealthProcessor(IBeatmap beatmap) => new OsuHealthProcessor(beatmap);
|
||||||
|
|
||||||
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap, this);
|
||||||
|
|
||||||
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
|
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
|
||||||
|
|
||||||
@@ -130,6 +132,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
return new Mod[]
|
return new Mod[]
|
||||||
{
|
{
|
||||||
new OsuModTarget(),
|
new OsuModTarget(),
|
||||||
|
new OsuModDifficultyAdjust(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
@@ -178,7 +181,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
|
|
||||||
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkinTransformer(source);
|
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkinTransformer(source);
|
||||||
|
|
||||||
public override int? LegacyID => 0;
|
public int LegacyID => 0;
|
||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Scoring
|
||||||
|
{
|
||||||
|
public class OsuHealthProcessor : HealthProcessor
|
||||||
|
{
|
||||||
|
public OsuHealthProcessor(IBeatmap beatmap)
|
||||||
|
: base(beatmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private float hpDrainRate;
|
||||||
|
|
||||||
|
protected override void ApplyBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
base.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
|
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double HealthAdjustmentFactorFor(JudgementResult result)
|
||||||
|
{
|
||||||
|
switch (result.Type)
|
||||||
|
{
|
||||||
|
case HitResult.Great:
|
||||||
|
return 10.2 - hpDrainRate;
|
||||||
|
|
||||||
|
case HitResult.Good:
|
||||||
|
return 8 - hpDrainRate;
|
||||||
|
|
||||||
|
case HitResult.Meh:
|
||||||
|
return 4 - hpDrainRate;
|
||||||
|
|
||||||
|
// case HitResult.SliderTick:
|
||||||
|
// return Math.Max(7 - hpDrainRate, 0) * 0.01;
|
||||||
|
|
||||||
|
case HitResult.Miss:
|
||||||
|
return hpDrainRate;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,39 +16,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private float hpDrainRate;
|
|
||||||
|
|
||||||
protected override void ApplyBeatmap(IBeatmap beatmap)
|
|
||||||
{
|
|
||||||
base.ApplyBeatmap(beatmap);
|
|
||||||
|
|
||||||
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override double HealthAdjustmentFactorFor(JudgementResult result)
|
|
||||||
{
|
|
||||||
switch (result.Type)
|
|
||||||
{
|
|
||||||
case HitResult.Great:
|
|
||||||
return 10.2 - hpDrainRate;
|
|
||||||
|
|
||||||
case HitResult.Good:
|
|
||||||
return 8 - hpDrainRate;
|
|
||||||
|
|
||||||
case HitResult.Meh:
|
|
||||||
return 4 - hpDrainRate;
|
|
||||||
|
|
||||||
// case HitResult.SliderTick:
|
|
||||||
// return Math.Max(7 - hpDrainRate, 0) * 0.01;
|
|
||||||
|
|
||||||
case HitResult.Miss:
|
|
||||||
return hpDrainRate;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement);
|
protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement);
|
||||||
|
|
||||||
public override HitWindows CreateHitWindows() => new OsuHitWindows();
|
public override HitWindows CreateHitWindows() => new OsuHitWindows();
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
|
|
||||||
private readonly bool isForCurrentRuleset;
|
private readonly bool isForCurrentRuleset;
|
||||||
|
|
||||||
public TaikoBeatmapConverter(IBeatmap beatmap)
|
public TaikoBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
|
||||||
: base(beatmap)
|
: base(beatmap, ruleset)
|
||||||
{
|
{
|
||||||
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(new TaikoRuleset().RulesetInfo);
|
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanConvert() => true;
|
public override bool CanConvert() => true;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Mods
|
||||||
|
{
|
||||||
|
public class TaikoModDifficultyAdjust : ModDifficultyAdjust
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Scoring
|
||||||
|
{
|
||||||
|
public class TaikoHealthProcessor : HealthProcessor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A value used for calculating <see cref="hpMultiplier"/>.
|
||||||
|
/// </summary>
|
||||||
|
private const double object_count_factor = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Taiko fails at the end of the map if the player has not half-filled their HP bar.
|
||||||
|
/// </summary>
|
||||||
|
protected override bool DefaultFailCondition => JudgedHits == MaxHits && Health.Value <= 0.5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HP multiplier for a successful <see cref="HitResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
private double hpMultiplier;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HP multiplier for a <see cref="HitResult.Miss"/>.
|
||||||
|
/// </summary>
|
||||||
|
private double hpMissMultiplier;
|
||||||
|
|
||||||
|
public TaikoHealthProcessor(IBeatmap beatmap)
|
||||||
|
: base(beatmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
base.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
|
hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.OfType<Hit>().Count() * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
|
||||||
|
|
||||||
|
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double HealthAdjustmentFactorFor(JudgementResult result)
|
||||||
|
=> result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier;
|
||||||
|
|
||||||
|
protected override void Reset(bool storeResults)
|
||||||
|
{
|
||||||
|
base.Reset(storeResults);
|
||||||
|
|
||||||
|
Health.Value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,60 +1,18 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Scoring
|
namespace osu.Game.Rulesets.Taiko.Scoring
|
||||||
{
|
{
|
||||||
internal class TaikoScoreProcessor : ScoreProcessor
|
internal class TaikoScoreProcessor : ScoreProcessor
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// A value used for calculating <see cref="hpMultiplier"/>.
|
|
||||||
/// </summary>
|
|
||||||
private const double object_count_factor = 3;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Taiko fails at the end of the map if the player has not half-filled their HP bar.
|
|
||||||
/// </summary>
|
|
||||||
protected override bool DefaultFailCondition => JudgedHits == MaxHits && Health.Value <= 0.5;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HP multiplier for a successful <see cref="HitResult"/>.
|
|
||||||
/// </summary>
|
|
||||||
private double hpMultiplier;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HP multiplier for a <see cref="HitResult.Miss"/>.
|
|
||||||
/// </summary>
|
|
||||||
private double hpMissMultiplier;
|
|
||||||
|
|
||||||
public TaikoScoreProcessor(IBeatmap beatmap)
|
public TaikoScoreProcessor(IBeatmap beatmap)
|
||||||
: base(beatmap)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyBeatmap(IBeatmap beatmap)
|
|
||||||
{
|
|
||||||
base.ApplyBeatmap(beatmap);
|
|
||||||
|
|
||||||
hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.OfType<Hit>().Count() * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
|
|
||||||
|
|
||||||
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override double HealthAdjustmentFactorFor(JudgementResult result)
|
|
||||||
=> result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier;
|
|
||||||
|
|
||||||
protected override void Reset(bool storeResults)
|
|
||||||
{
|
|
||||||
base.Reset(storeResults);
|
|
||||||
|
|
||||||
Health.Value = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override HitWindows CreateHitWindows() => new TaikoHitWindows();
|
public override HitWindows CreateHitWindows() => new TaikoHitWindows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,15 @@ using System;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko
|
namespace osu.Game.Rulesets.Taiko
|
||||||
{
|
{
|
||||||
public class TaikoRuleset : Ruleset
|
public class TaikoRuleset : Ruleset, ILegacyRuleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableTaikoRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableTaikoRuleset(this, beatmap, mods);
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new TaikoScoreProcessor(beatmap);
|
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new TaikoScoreProcessor(beatmap);
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
|
public override HealthProcessor CreateHealthProcessor(IBeatmap beatmap) => new TaikoHealthProcessor(beatmap);
|
||||||
|
|
||||||
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap, this);
|
||||||
|
|
||||||
public const string SHORT_NAME = "taiko";
|
public const string SHORT_NAME = "taiko";
|
||||||
|
|
||||||
@@ -105,6 +107,12 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
new TaikoModFlashlight(),
|
new TaikoModFlashlight(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case ModType.Conversion:
|
||||||
|
return new Mod[]
|
||||||
|
{
|
||||||
|
new TaikoModDifficultyAdjust(),
|
||||||
|
};
|
||||||
|
|
||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
return new Mod[]
|
return new Mod[]
|
||||||
{
|
{
|
||||||
@@ -133,7 +141,7 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
|
|
||||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new TaikoPerformanceCalculator(this, beatmap, score);
|
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new TaikoPerformanceCalculator(this, beatmap, score);
|
||||||
|
|
||||||
public override int? LegacyID => 1;
|
public int LegacyID => 1;
|
||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using osu.Game.Rulesets.Objects.Types;
|
|||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
|
using osu.Game.Rulesets.Catch;
|
||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@@ -313,7 +314,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var beatmap = decoder.Decode(stream);
|
var beatmap = decoder.Decode(stream);
|
||||||
|
|
||||||
var converted = new OsuBeatmapConverter(beatmap).Convert();
|
var converted = new OsuBeatmapConverter(beatmap, new OsuRuleset()).Convert();
|
||||||
new OsuBeatmapProcessor(converted).PreProcess();
|
new OsuBeatmapProcessor(converted).PreProcess();
|
||||||
new OsuBeatmapProcessor(converted).PostProcess();
|
new OsuBeatmapProcessor(converted).PostProcess();
|
||||||
|
|
||||||
@@ -336,7 +337,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var beatmap = decoder.Decode(stream);
|
var beatmap = decoder.Decode(stream);
|
||||||
|
|
||||||
var converted = new CatchBeatmapConverter(beatmap).Convert();
|
var converted = new CatchBeatmapConverter(beatmap, new CatchRuleset()).Convert();
|
||||||
new CatchBeatmapProcessor(converted).PreProcess();
|
new CatchBeatmapProcessor(converted).PreProcess();
|
||||||
new CatchBeatmapProcessor(converted).PostProcess();
|
new CatchBeatmapProcessor(converted).PostProcess();
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
switch (global)
|
switch (global)
|
||||||
{
|
{
|
||||||
case GlobalSkinConfiguration.ComboColours:
|
case GlobalSkinConfiguration.ComboColours:
|
||||||
return SkinUtils.As<TValue>(new Bindable<List<Color4>>(ComboColours));
|
return SkinUtils.As<TValue>(new Bindable<IReadOnlyList<Color4>>(ComboColours));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -13,31 +13,22 @@ namespace osu.Game.Tests.Skins
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class LegacySkinDecoderTest
|
public class LegacySkinDecoderTest
|
||||||
{
|
{
|
||||||
[TestCase(true)]
|
[Test]
|
||||||
[TestCase(false)]
|
public void TestDecodeSkinColours()
|
||||||
public void TestDecodeSkinColours(bool hasColours)
|
|
||||||
{
|
{
|
||||||
var decoder = new LegacySkinDecoder();
|
var decoder = new LegacySkinDecoder();
|
||||||
|
|
||||||
using (var resStream = TestResources.OpenResource(hasColours ? "skin.ini" : "skin-empty.ini"))
|
using (var resStream = TestResources.OpenResource("skin.ini"))
|
||||||
using (var stream = new LineBufferedReader(resStream))
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
{
|
{
|
||||||
var comboColors = decoder.Decode(stream).ComboColours;
|
var comboColors = decoder.Decode(stream).ComboColours;
|
||||||
|
var expectedColors = new List<Color4>
|
||||||
List<Color4> expectedColors;
|
|
||||||
|
|
||||||
if (hasColours)
|
|
||||||
{
|
{
|
||||||
expectedColors = new List<Color4>
|
new Color4(142, 199, 255, 255),
|
||||||
{
|
new Color4(255, 128, 128, 255),
|
||||||
new Color4(142, 199, 255, 255),
|
new Color4(128, 255, 255, 255),
|
||||||
new Color4(255, 128, 128, 255),
|
new Color4(100, 100, 100, 100),
|
||||||
new Color4(128, 255, 255, 255),
|
};
|
||||||
new Color4(100, 100, 100, 100),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
expectedColors = new DefaultSkin().Configuration.ComboColours;
|
|
||||||
|
|
||||||
Assert.AreEqual(expectedColors.Count, comboColors.Count);
|
Assert.AreEqual(expectedColors.Count, comboColors.Count);
|
||||||
for (int i = 0; i < expectedColors.Count; i++)
|
for (int i = 0; i < expectedColors.Count; i++)
|
||||||
@@ -45,6 +36,37 @@ namespace osu.Game.Tests.Skins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeEmptySkinColours()
|
||||||
|
{
|
||||||
|
var decoder = new LegacySkinDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("skin-empty.ini"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var comboColors = decoder.Decode(stream).ComboColours;
|
||||||
|
var expectedColors = SkinConfiguration.DefaultComboColours;
|
||||||
|
|
||||||
|
Assert.AreEqual(expectedColors.Count, comboColors.Count);
|
||||||
|
for (int i = 0; i < expectedColors.Count; i++)
|
||||||
|
Assert.AreEqual(expectedColors[i], comboColors[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeEmptySkinColoursNoFallback()
|
||||||
|
{
|
||||||
|
var decoder = new LegacySkinDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("skin-empty.ini"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var skinConfiguration = decoder.Decode(stream);
|
||||||
|
skinConfiguration.AllowDefaultComboColoursFallback = false;
|
||||||
|
Assert.IsNull(skinConfiguration.ComboColours);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeGeneral()
|
public void TestDecodeGeneral()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
@@ -21,8 +22,8 @@ namespace osu.Game.Tests.Skins
|
|||||||
[HeadlessTest]
|
[HeadlessTest]
|
||||||
public class TestSceneSkinConfigurationLookup : OsuTestScene
|
public class TestSceneSkinConfigurationLookup : OsuTestScene
|
||||||
{
|
{
|
||||||
private LegacySkin source1;
|
private SkinSource source1;
|
||||||
private LegacySkin source2;
|
private SkinSource source2;
|
||||||
private SkinRequester requester;
|
private SkinRequester requester;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
@@ -94,7 +95,7 @@ namespace osu.Game.Tests.Skins
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestGlobalLookup()
|
public void TestGlobalLookup()
|
||||||
{
|
{
|
||||||
AddAssert("Check combo colours", () => requester.GetConfig<GlobalSkinConfiguration, List<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0);
|
AddAssert("Check combo colours", () => requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@@ -116,6 +117,28 @@ namespace osu.Game.Tests.Skins
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEmptyComboColours()
|
||||||
|
{
|
||||||
|
AddAssert("Check retrieved combo colours is skin default colours", () =>
|
||||||
|
requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.SequenceEqual(SkinConfiguration.DefaultComboColours) ?? false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEmptyComboColoursNoFallback()
|
||||||
|
{
|
||||||
|
AddStep("Add custom combo colours to source1", () => source1.Configuration.AddComboColours(
|
||||||
|
new Color4(100, 150, 200, 255),
|
||||||
|
new Color4(55, 110, 166, 255),
|
||||||
|
new Color4(75, 125, 175, 255)
|
||||||
|
));
|
||||||
|
|
||||||
|
AddStep("Disallow default colours fallback in source2", () => source2.Configuration.AllowDefaultComboColoursFallback = false);
|
||||||
|
|
||||||
|
AddAssert("Check retrieved combo colours from source1", () =>
|
||||||
|
requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.SequenceEqual(source1.Configuration.ComboColours) ?? false);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestLegacyVersionLookup()
|
public void TestLegacyVersionLookup()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new TestDrawableScrollingRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new TestDrawableScrollingRuleset(this, beatmap, mods);
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap);
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap, null);
|
||||||
|
|
||||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException();
|
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException();
|
||||||
|
|
||||||
@@ -268,8 +268,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private class TestBeatmapConverter : BeatmapConverter<TestHitObject>
|
private class TestBeatmapConverter : BeatmapConverter<TestHitObject>
|
||||||
{
|
{
|
||||||
public TestBeatmapConverter(IBeatmap beatmap)
|
public TestBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
|
||||||
: base(beatmap)
|
: base(beatmap, ruleset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
ScoreProcessor.FailConditions += (_, __) => true;
|
HealthProcessor.FailConditions += (_, __) => true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
AddUntilStep("wait for fail", () => Player.HasFailed);
|
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||||
AddUntilStep("wait for multiple judged objects", () => ((FailPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.Count(h => h.AllJudged) > 1);
|
AddUntilStep("wait for multiple judged objects", () => ((FailPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.Count(h => h.AllJudged) > 1);
|
||||||
AddAssert("total judgements == 1", () => ((FailPlayer)Player).ScoreProcessor.JudgedHits == 1);
|
AddAssert("total judgements == 1", () => ((FailPlayer)Player).HealthProcessor.JudgedHits >= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FailPlayer : TestPlayer
|
private class FailPlayer : TestPlayer
|
||||||
{
|
{
|
||||||
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
public new HealthProcessor HealthProcessor => base.HealthProcessor;
|
||||||
|
|
||||||
public FailPlayer()
|
public FailPlayer()
|
||||||
: base(false, false)
|
: base(false, false)
|
||||||
@@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
ScoreProcessor.FailConditions += (_, __) => true;
|
HealthProcessor.FailConditions += (_, __) => true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
AddStep("create overlay", () =>
|
AddStep("create overlay", () =>
|
||||||
{
|
{
|
||||||
Child = hudOverlay = new HUDOverlay(null, null, Array.Empty<Mod>());
|
Child = hudOverlay = new HUDOverlay(null, null, null, Array.Empty<Mod>());
|
||||||
|
|
||||||
action?.Invoke(hudOverlay);
|
action?.Invoke(hudOverlay);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public void TestResumeWithResumeOverlay()
|
public void TestResumeWithResumeOverlay()
|
||||||
{
|
{
|
||||||
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
|
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
|
||||||
AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1);
|
AddUntilStep("wait for hitobjects", () => Player.HealthProcessor.Health.Value < 1);
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
resume();
|
resume();
|
||||||
@@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public void TestPauseWithResumeOverlay()
|
public void TestPauseWithResumeOverlay()
|
||||||
{
|
{
|
||||||
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
|
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
|
||||||
AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1);
|
AddUntilStep("wait for hitobjects", () => Player.HealthProcessor.Health.Value < 1);
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
AddStep("move cursor to button", () =>
|
AddStep("move cursor to button", () =>
|
||||||
InputManager.MoveMouseTo(Player.HUDOverlay.HoldToQuit.Children.OfType<HoldToConfirmContainer>().First().ScreenSpaceDrawQuad.Centre));
|
InputManager.MoveMouseTo(Player.HUDOverlay.HoldToQuit.Children.OfType<HoldToConfirmContainer>().First().ScreenSpaceDrawQuad.Centre));
|
||||||
AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1);
|
AddUntilStep("wait for hitobjects", () => Player.HealthProcessor.Health.Value < 1);
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
resumeAndConfirm();
|
resumeAndConfirm();
|
||||||
@@ -285,7 +285,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
protected class PausePlayer : TestPlayer
|
protected class PausePlayer : TestPlayer
|
||||||
{
|
{
|
||||||
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
public new HealthProcessor HealthProcessor => base.HealthProcessor;
|
||||||
|
|
||||||
public new HUDOverlay HUDOverlay => base.HUDOverlay;
|
public new HUDOverlay HUDOverlay => base.HUDOverlay;
|
||||||
|
|
||||||
|
|||||||
@@ -1,237 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
using osu.Game.Screens.Select;
|
|
||||||
using osu.Game.Tests.Beatmaps;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.SongSelect
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
[System.ComponentModel.Description("PlaySongSelect leaderboard/details area")]
|
|
||||||
public class TestSceneBeatmapDetailArea : OsuTestScene
|
|
||||||
{
|
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BeatmapDetails) };
|
|
||||||
|
|
||||||
private ModDisplay modDisplay;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuGameBase game, RulesetStore rulesets)
|
|
||||||
{
|
|
||||||
BeatmapDetailArea detailsArea;
|
|
||||||
Add(detailsArea = new BeatmapDetailArea
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Size = new Vector2(550f, 450f),
|
|
||||||
});
|
|
||||||
|
|
||||||
Add(modDisplay = new ModDisplay
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Position = new Vector2(0, 25),
|
|
||||||
});
|
|
||||||
|
|
||||||
modDisplay.Current.BindTo(SelectedMods);
|
|
||||||
|
|
||||||
AddStep("all metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
|
||||||
{
|
|
||||||
BeatmapInfo =
|
|
||||||
{
|
|
||||||
BeatmapSet = new BeatmapSetInfo
|
|
||||||
{
|
|
||||||
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
|
||||||
},
|
|
||||||
Version = "All Metrics",
|
|
||||||
Metadata = new BeatmapMetadata
|
|
||||||
{
|
|
||||||
Source = "osu!lazer",
|
|
||||||
Tags = "this beatmap has all the metrics",
|
|
||||||
},
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 7,
|
|
||||||
DrainRate = 1,
|
|
||||||
OverallDifficulty = 5.7f,
|
|
||||||
ApproachRate = 3.5f,
|
|
||||||
},
|
|
||||||
StarDifficulty = 5.3f,
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
AddStep("all except source", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
|
||||||
{
|
|
||||||
BeatmapInfo =
|
|
||||||
{
|
|
||||||
BeatmapSet = new BeatmapSetInfo
|
|
||||||
{
|
|
||||||
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
|
||||||
},
|
|
||||||
Version = "All Metrics",
|
|
||||||
Metadata = new BeatmapMetadata
|
|
||||||
{
|
|
||||||
Tags = "this beatmap has all the metrics",
|
|
||||||
},
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 7,
|
|
||||||
DrainRate = 1,
|
|
||||||
OverallDifficulty = 5.7f,
|
|
||||||
ApproachRate = 3.5f,
|
|
||||||
},
|
|
||||||
StarDifficulty = 5.3f,
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
AddStep("ratings", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
|
||||||
{
|
|
||||||
BeatmapInfo =
|
|
||||||
{
|
|
||||||
BeatmapSet = new BeatmapSetInfo
|
|
||||||
{
|
|
||||||
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
|
||||||
},
|
|
||||||
Version = "Only Ratings",
|
|
||||||
Metadata = new BeatmapMetadata
|
|
||||||
{
|
|
||||||
Source = "osu!lazer",
|
|
||||||
Tags = "this beatmap has ratings metrics but not retries or fails",
|
|
||||||
},
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 6,
|
|
||||||
DrainRate = 9,
|
|
||||||
OverallDifficulty = 6,
|
|
||||||
ApproachRate = 6,
|
|
||||||
},
|
|
||||||
StarDifficulty = 4.8f
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
AddStep("fails+retries", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
|
||||||
{
|
|
||||||
BeatmapInfo =
|
|
||||||
{
|
|
||||||
Version = "Only Retries and Fails",
|
|
||||||
Metadata = new BeatmapMetadata
|
|
||||||
{
|
|
||||||
Source = "osu!lazer",
|
|
||||||
Tags = "this beatmap has retries and fails but no ratings",
|
|
||||||
},
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 3.7f,
|
|
||||||
DrainRate = 6,
|
|
||||||
OverallDifficulty = 6,
|
|
||||||
ApproachRate = 7,
|
|
||||||
},
|
|
||||||
StarDifficulty = 2.91f,
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
AddStep("null metrics", () => detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
|
||||||
{
|
|
||||||
BeatmapInfo =
|
|
||||||
{
|
|
||||||
Version = "No Metrics",
|
|
||||||
Metadata = new BeatmapMetadata
|
|
||||||
{
|
|
||||||
Source = "osu!lazer",
|
|
||||||
Tags = "this beatmap has no metrics",
|
|
||||||
},
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 5,
|
|
||||||
DrainRate = 5,
|
|
||||||
OverallDifficulty = 5.5f,
|
|
||||||
ApproachRate = 6.5f,
|
|
||||||
},
|
|
||||||
StarDifficulty = 1.97f,
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
AddStep("null beatmap", () => detailsArea.Beatmap = null);
|
|
||||||
|
|
||||||
Ruleset ruleset = rulesets.AvailableRulesets.First().CreateInstance();
|
|
||||||
|
|
||||||
AddStep("with EZ mod", () =>
|
|
||||||
{
|
|
||||||
detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
|
||||||
{
|
|
||||||
BeatmapInfo =
|
|
||||||
{
|
|
||||||
Version = "Has Easy Mod",
|
|
||||||
Metadata = new BeatmapMetadata
|
|
||||||
{
|
|
||||||
Source = "osu!lazer",
|
|
||||||
Tags = "this beatmap has the easy mod enabled",
|
|
||||||
},
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 3,
|
|
||||||
DrainRate = 3,
|
|
||||||
OverallDifficulty = 3,
|
|
||||||
ApproachRate = 3,
|
|
||||||
},
|
|
||||||
StarDifficulty = 1f,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModEasy) };
|
|
||||||
});
|
|
||||||
|
|
||||||
AddStep("with HR mod", () =>
|
|
||||||
{
|
|
||||||
detailsArea.Beatmap = new TestWorkingBeatmap(new Beatmap
|
|
||||||
{
|
|
||||||
BeatmapInfo =
|
|
||||||
{
|
|
||||||
Version = "Has Hard Rock Mod",
|
|
||||||
Metadata = new BeatmapMetadata
|
|
||||||
{
|
|
||||||
Source = "osu!lazer",
|
|
||||||
Tags = "this beatmap has the hard rock mod enabled",
|
|
||||||
},
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 3,
|
|
||||||
DrainRate = 3,
|
|
||||||
OverallDifficulty = 3,
|
|
||||||
ApproachRate = 3,
|
|
||||||
},
|
|
||||||
StarDifficulty = 1f,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModHardRock) };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,8 +3,14 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.SongSelect
|
namespace osu.Game.Tests.Visual.SongSelect
|
||||||
@@ -174,5 +180,27 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
OnlineBeatmapID = 162,
|
OnlineBeatmapID = 162,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesets { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestModAdjustments()
|
||||||
|
{
|
||||||
|
TestAllMetrics();
|
||||||
|
|
||||||
|
Ruleset ruleset = rulesets.AvailableRulesets.First().CreateInstance();
|
||||||
|
|
||||||
|
AddStep("with EZ mod", () => SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModEasy) });
|
||||||
|
|
||||||
|
AddAssert("first bar coloured blue", () => details.ChildrenOfType<Bar>().Skip(1).First().AccentColour == colours.BlueDark);
|
||||||
|
|
||||||
|
AddStep("with HR mod", () => SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModHardRock) });
|
||||||
|
|
||||||
|
AddAssert("first bar coloured red", () => details.ChildrenOfType<Bar>().Skip(1).First().AccentColour == colours.Red);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
@@ -25,7 +26,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public IBeatmap Beatmap { get; }
|
public IBeatmap Beatmap { get; }
|
||||||
|
|
||||||
protected BeatmapConverter(IBeatmap beatmap)
|
protected BeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
|
||||||
{
|
{
|
||||||
Beatmap = beatmap;
|
Beatmap = beatmap;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
public interface IHasComboColours
|
public interface IHasComboColours
|
||||||
{
|
{
|
||||||
List<Color4> ComboColours { get; set; }
|
/// <summary>
|
||||||
|
/// Retrieves the list of combo colours for presentation only.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList<Color4> ComboColours { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds combo colours to the list.
|
||||||
|
/// </summary>
|
||||||
|
void AddComboColours(params Color4[] colours);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,8 +77,6 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool hasComboColours;
|
|
||||||
|
|
||||||
private void handleColours(T output, string line)
|
private void handleColours(T output, string line)
|
||||||
{
|
{
|
||||||
var pair = SplitKeyVal(line);
|
var pair = SplitKeyVal(line);
|
||||||
@@ -105,14 +103,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
if (!(output is IHasComboColours tHasComboColours)) return;
|
if (!(output is IHasComboColours tHasComboColours)) return;
|
||||||
|
|
||||||
if (!hasComboColours)
|
tHasComboColours.AddComboColours(colour);
|
||||||
{
|
|
||||||
// remove default colours.
|
|
||||||
tHasComboColours.ComboColours.Clear();
|
|
||||||
hasComboColours = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
tHasComboColours.ComboColours.Add(colour);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
public override bool HandleNonPositionalInput => State == Visibility.Visible;
|
public override bool HandleNonPositionalInput => State == Visibility.Visible;
|
||||||
public override bool HandlePositionalInput => State == Visibility.Visible;
|
public override bool HandlePositionalInput => State == Visibility.Visible;
|
||||||
public override bool IsRemovable => true;
|
|
||||||
|
|
||||||
private Visibility state;
|
private Visibility state;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@@ -31,6 +32,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
protected readonly Nub Nub;
|
protected readonly Nub Nub;
|
||||||
private readonly Box leftBox;
|
private readonly Box leftBox;
|
||||||
private readonly Box rightBox;
|
private readonly Box rightBox;
|
||||||
|
private readonly Container nubContainer;
|
||||||
|
|
||||||
public virtual string TooltipText { get; private set; }
|
public virtual string TooltipText { get; private set; }
|
||||||
|
|
||||||
@@ -72,10 +74,15 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Alpha = 0.5f,
|
Alpha = 0.5f,
|
||||||
},
|
},
|
||||||
Nub = new Nub
|
nubContainer = new Container
|
||||||
{
|
{
|
||||||
Origin = Anchor.TopCentre,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Expanded = true,
|
Child = Nub = new Nub
|
||||||
|
{
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
RelativePositionAxes = Axes.X,
|
||||||
|
Expanded = true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
new HoverClickSounds()
|
new HoverClickSounds()
|
||||||
};
|
};
|
||||||
@@ -90,6 +97,13 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
AccentColour = colours.Pink;
|
AccentColour = colours.Pink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
nubContainer.Padding = new MarginPadding { Horizontal = RangePadding };
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
@@ -176,14 +190,14 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
base.UpdateAfterChildren();
|
base.UpdateAfterChildren();
|
||||||
leftBox.Scale = new Vector2(Math.Clamp(
|
leftBox.Scale = new Vector2(Math.Clamp(
|
||||||
Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1);
|
RangePadding + Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1);
|
||||||
rightBox.Scale = new Vector2(Math.Clamp(
|
rightBox.Scale = new Vector2(Math.Clamp(
|
||||||
DrawWidth - Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1);
|
DrawWidth - Nub.DrawPosition.X - RangePadding - Nub.DrawWidth / 2, 0, DrawWidth), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateValue(float value)
|
protected override void UpdateValue(float value)
|
||||||
{
|
{
|
||||||
Nub.MoveToX(RangePadding + UsableWidth * value, 250, Easing.OutQuint);
|
Nub.MoveToX(value, 250, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ using osu.Framework.Input.Events;
|
|||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class OsuTextBox : TextBox
|
public class OsuTextBox : BasicTextBox
|
||||||
{
|
{
|
||||||
protected override float LeftRightPadding => 10;
|
protected override float LeftRightPadding => 10;
|
||||||
|
|
||||||
|
protected override float CaretWidth => 3;
|
||||||
|
|
||||||
protected override SpriteText CreatePlaceholder() => new OsuSpriteText
|
protected override SpriteText CreatePlaceholder() => new OsuSpriteText
|
||||||
{
|
{
|
||||||
Font = OsuFont.GetFont(italics: true),
|
Font = OsuFont.GetFont(italics: true),
|
||||||
@@ -41,6 +43,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
BackgroundCommit = BorderColour = colour.Yellow;
|
BackgroundCommit = BorderColour = colour.Yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Color4 SelectionColour => new Color4(249, 90, 255, 255);
|
||||||
|
|
||||||
protected override void OnFocus(FocusEvent e)
|
protected override void OnFocus(FocusEvent e)
|
||||||
{
|
{
|
||||||
BorderThickness = 3;
|
BorderThickness = 3;
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ namespace osu.Game.Overlays
|
|||||||
d.Origin = Anchor.BottomLeft;
|
d.Origin = Anchor.BottomLeft;
|
||||||
d.RelativeSizeAxes = Axes.Both;
|
d.RelativeSizeAxes = Axes.Both;
|
||||||
d.OnRequestLeave = channelManager.LeaveChannel;
|
d.OnRequestLeave = channelManager.LeaveChannel;
|
||||||
|
d.IsSwitchable = true;
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Settings
|
||||||
|
{
|
||||||
|
public interface ISettingsItem : IDrawable, IDisposable
|
||||||
|
{
|
||||||
|
event Action SettingChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@@ -20,7 +21,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public abstract class SettingsItem<T> : Container, IFilterable
|
public abstract class SettingsItem<T> : Container, IFilterable, ISettingsItem
|
||||||
{
|
{
|
||||||
protected abstract Drawable CreateControl();
|
protected abstract Drawable CreateControl();
|
||||||
|
|
||||||
@@ -34,8 +35,6 @@ namespace osu.Game.Overlays.Settings
|
|||||||
|
|
||||||
private SpriteText text;
|
private SpriteText text;
|
||||||
|
|
||||||
private readonly RestoreDefaultValueButton restoreDefaultButton;
|
|
||||||
|
|
||||||
public bool ShowsDefaultIndicator = true;
|
public bool ShowsDefaultIndicator = true;
|
||||||
|
|
||||||
public virtual string LabelText
|
public virtual string LabelText
|
||||||
@@ -70,8 +69,12 @@ namespace osu.Game.Overlays.Settings
|
|||||||
|
|
||||||
public bool FilteringActive { get; set; }
|
public bool FilteringActive { get; set; }
|
||||||
|
|
||||||
|
public event Action SettingChanged;
|
||||||
|
|
||||||
protected SettingsItem()
|
protected SettingsItem()
|
||||||
{
|
{
|
||||||
|
RestoreDefaultValueButton restoreDefaultButton;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS };
|
Padding = new MarginPadding { Right = SettingsPanel.CONTENT_MARGINS };
|
||||||
@@ -87,13 +90,12 @@ namespace osu.Game.Overlays.Settings
|
|||||||
Child = Control = CreateControl()
|
Child = Control = CreateControl()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
// all bindable logic is in constructor intentionally to support "CreateSettingsControls" being used in a context it is
|
||||||
private void load()
|
// never loaded, but requires bindable storage.
|
||||||
{
|
|
||||||
if (controlWithCurrent != null)
|
if (controlWithCurrent != null)
|
||||||
{
|
{
|
||||||
|
controlWithCurrent.Current.ValueChanged += _ => SettingChanged?.Invoke();
|
||||||
controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; };
|
controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; };
|
||||||
|
|
||||||
if (ShowsDefaultIndicator)
|
if (ShowsDefaultIndicator)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets
|
||||||
|
{
|
||||||
|
public interface ILegacyRuleset
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the server-side ID of a legacy ruleset.
|
||||||
|
/// </summary>
|
||||||
|
int LegacyID { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mods
|
||||||
|
{
|
||||||
|
public interface IApplicableToHealthProcessor : IApplicableMod
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provide a <see cref="HealthProcessor"/> to a mod. Called once on initialisation of a play instance.
|
||||||
|
/// </summary>
|
||||||
|
void ApplyToHealthProcessor(HealthProcessor healthProcessor);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using System;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mods
|
||||||
|
{
|
||||||
|
public abstract class ModDifficultyAdjust : Mod, IApplicableToDifficulty
|
||||||
|
{
|
||||||
|
public override string Name => @"Difficulty Adjust";
|
||||||
|
|
||||||
|
public override string Description => @"Override a beatmap's difficulty settings.";
|
||||||
|
|
||||||
|
public override string Acronym => "DA";
|
||||||
|
|
||||||
|
public override ModType Type => ModType.Conversion;
|
||||||
|
|
||||||
|
public override IconUsage Icon => FontAwesome.Solid.Hammer;
|
||||||
|
|
||||||
|
public override double ScoreMultiplier => 1.0;
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModHardRock) };
|
||||||
|
|
||||||
|
[SettingSource("Drain Rate", "Override a beatmap's set HP.")]
|
||||||
|
public BindableNumber<float> DrainRate { get; } = new BindableFloat
|
||||||
|
{
|
||||||
|
Precision = 0.1f,
|
||||||
|
MinValue = 1,
|
||||||
|
MaxValue = 10,
|
||||||
|
Default = 5,
|
||||||
|
Value = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Overall Difficulty", "Override a beatmap's set OD.")]
|
||||||
|
public BindableNumber<float> OverallDifficulty { get; } = new BindableFloat
|
||||||
|
{
|
||||||
|
Precision = 0.1f,
|
||||||
|
MinValue = 1,
|
||||||
|
MaxValue = 10,
|
||||||
|
Default = 5,
|
||||||
|
Value = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
private BeatmapDifficulty difficulty;
|
||||||
|
|
||||||
|
public void ApplyToDifficulty(BeatmapDifficulty difficulty)
|
||||||
|
{
|
||||||
|
if (this.difficulty == null || this.difficulty.ID != difficulty.ID)
|
||||||
|
{
|
||||||
|
this.difficulty = difficulty;
|
||||||
|
TransferSettings(difficulty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ApplySettings(difficulty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transfer initial settings from the beatmap to settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="difficulty">The beatmap's initial values.</param>
|
||||||
|
protected virtual void TransferSettings(BeatmapDifficulty difficulty)
|
||||||
|
{
|
||||||
|
DrainRate.Value = DrainRate.Default = difficulty.DrainRate;
|
||||||
|
OverallDifficulty.Value = OverallDifficulty.Default = difficulty.OverallDifficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apply all custom settings to the provided beatmap.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="difficulty">The beatmap to have settings applied.</param>
|
||||||
|
protected virtual void ApplySettings(BeatmapDifficulty difficulty)
|
||||||
|
{
|
||||||
|
difficulty.DrainRate = DrainRate.Value;
|
||||||
|
difficulty.OverallDifficulty = OverallDifficulty.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,13 +5,13 @@ using System;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
public abstract class ModEasy : Mod, IApplicableToDifficulty, IApplicableFailOverride, IApplicableToScoreProcessor
|
public abstract class ModEasy : Mod, IApplicableToDifficulty, IApplicableFailOverride, IApplicableToHealthProcessor
|
||||||
{
|
{
|
||||||
public override string Name => "Easy";
|
public override string Name => "Easy";
|
||||||
public override string Acronym => "EZ";
|
public override string Acronym => "EZ";
|
||||||
@@ -19,9 +19,16 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override ModType Type => ModType.DifficultyReduction;
|
public override ModType Type => ModType.DifficultyReduction;
|
||||||
public override double ScoreMultiplier => 0.5;
|
public override double ScoreMultiplier => 0.5;
|
||||||
public override bool Ranked => true;
|
public override bool Ranked => true;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock), typeof(ModDifficultyAdjust) };
|
||||||
|
|
||||||
private int retries = 2;
|
[SettingSource("Extra Lives", "Number of extra lives")]
|
||||||
|
public Bindable<int> Retries { get; } = new BindableInt(2)
|
||||||
|
{
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
private int retries;
|
||||||
|
|
||||||
private BindableNumber<double> health;
|
private BindableNumber<double> health;
|
||||||
|
|
||||||
@@ -32,6 +39,8 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
difficulty.ApproachRate *= ratio;
|
difficulty.ApproachRate *= ratio;
|
||||||
difficulty.DrainRate *= ratio;
|
difficulty.DrainRate *= ratio;
|
||||||
difficulty.OverallDifficulty *= ratio;
|
difficulty.OverallDifficulty *= ratio;
|
||||||
|
|
||||||
|
retries = Retries.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AllowFail
|
public bool AllowFail
|
||||||
@@ -49,11 +58,9 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
public bool RestartOnFail => false;
|
public bool RestartOnFail => false;
|
||||||
|
|
||||||
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
|
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||||
{
|
{
|
||||||
health = scoreProcessor.Health.GetBoundCopy();
|
health = healthProcessor.Health.GetBoundCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override IconUsage Icon => OsuIcon.ModHardrock;
|
public override IconUsage Icon => OsuIcon.ModHardrock;
|
||||||
public override ModType Type => ModType.DifficultyIncrease;
|
public override ModType Type => ModType.DifficultyIncrease;
|
||||||
public override string Description => "Everything just got a bit harder...";
|
public override string Description => "Everything just got a bit harder...";
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModEasy) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust) };
|
||||||
|
|
||||||
public void ApplyToDifficulty(BeatmapDifficulty difficulty)
|
public void ApplyToDifficulty(BeatmapDifficulty difficulty)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override IconUsage Icon => OsuIcon.ModPerfect;
|
public override IconUsage Icon => OsuIcon.ModPerfect;
|
||||||
public override string Description => "SS or quit.";
|
public override string Description => "SS or quit.";
|
||||||
|
|
||||||
protected override bool FailCondition(ScoreProcessor scoreProcessor, JudgementResult result) => scoreProcessor.Accuracy.Value != 1;
|
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => result.Type != result.Judgement.MaxResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor, IApplicableFailOverride
|
public abstract class ModSuddenDeath : Mod, IApplicableToHealthProcessor, IApplicableFailOverride
|
||||||
{
|
{
|
||||||
public override string Name => "Sudden Death";
|
public override string Name => "Sudden Death";
|
||||||
public override string Acronym => "SD";
|
public override string Acronym => "SD";
|
||||||
@@ -24,13 +23,11 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public bool AllowFail => true;
|
public bool AllowFail => true;
|
||||||
public bool RestartOnFail => true;
|
public bool RestartOnFail => true;
|
||||||
|
|
||||||
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
|
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||||
{
|
{
|
||||||
scoreProcessor.FailConditions += FailCondition;
|
healthProcessor.FailConditions += FailCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
protected virtual bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => !result.IsHit && result.Judgement.AffectsCombo;
|
||||||
|
|
||||||
protected virtual bool FailCondition(ScoreProcessor scoreProcessor, JudgementResult result) => scoreProcessor.Combo.Value == 0 && result.Judgement.AffectsCombo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
{
|
{
|
||||||
if (HitObject is IHasComboInformation combo)
|
if (HitObject is IHasComboInformation combo)
|
||||||
{
|
{
|
||||||
var comboColours = CurrentSkin.GetConfig<GlobalSkinConfiguration, List<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value;
|
var comboColours = CurrentSkin.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value;
|
||||||
AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White;
|
AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets
|
|||||||
{
|
{
|
||||||
public abstract class Ruleset
|
public abstract class Ruleset
|
||||||
{
|
{
|
||||||
public readonly RulesetInfo RulesetInfo;
|
public RulesetInfo RulesetInfo { get; internal set; }
|
||||||
|
|
||||||
public IEnumerable<Mod> GetAllMods() => Enum.GetValues(typeof(ModType)).Cast<ModType>()
|
public IEnumerable<Mod> GetAllMods() => Enum.GetValues(typeof(ModType)).Cast<ModType>()
|
||||||
// Confine all mods of each mod type into a single IEnumerable<Mod>
|
// Confine all mods of each mod type into a single IEnumerable<Mod>
|
||||||
@@ -51,7 +51,14 @@ namespace osu.Game.Rulesets
|
|||||||
|
|
||||||
protected Ruleset()
|
protected Ruleset()
|
||||||
{
|
{
|
||||||
RulesetInfo = createRulesetInfo();
|
RulesetInfo = new RulesetInfo
|
||||||
|
{
|
||||||
|
Name = Description,
|
||||||
|
ShortName = ShortName,
|
||||||
|
ID = (this as ILegacyRuleset)?.LegacyID,
|
||||||
|
InstantiationInfo = GetType().AssemblyQualifiedName,
|
||||||
|
Available = true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -69,6 +76,12 @@ namespace osu.Game.Rulesets
|
|||||||
/// <returns>The score processor.</returns>
|
/// <returns>The score processor.</returns>
|
||||||
public virtual ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new ScoreProcessor(beatmap);
|
public virtual ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new ScoreProcessor(beatmap);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="HealthProcessor"/> for a beatmap converted to this ruleset.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The health processor.</returns>
|
||||||
|
public virtual HealthProcessor CreateHealthProcessor(IBeatmap beatmap) => new HealthProcessor(beatmap);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="IBeatmapConverter"/> to convert a <see cref="IBeatmap"/> to one that is applicable for this <see cref="Ruleset"/>.
|
/// Creates a <see cref="IBeatmapConverter"/> to convert a <see cref="IBeatmap"/> to one that is applicable for this <see cref="Ruleset"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -103,11 +116,6 @@ namespace osu.Game.Rulesets
|
|||||||
/// <param name="settings">The <see cref="SettingsStore"/> to store the settings.</param>
|
/// <param name="settings">The <see cref="SettingsStore"/> to store the settings.</param>
|
||||||
public virtual IRulesetConfigManager CreateConfig(SettingsStore settings) => null;
|
public virtual IRulesetConfigManager CreateConfig(SettingsStore settings) => null;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Do not override this unless you are a legacy mode.
|
|
||||||
/// </summary>
|
|
||||||
public virtual int? LegacyID => null;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A unique short name to reference this ruleset in online requests.
|
/// A unique short name to reference this ruleset in online requests.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -138,18 +146,5 @@ namespace osu.Game.Rulesets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>An empty frame for the current ruleset, or null if unsupported.</returns>
|
/// <returns>An empty frame for the current ruleset, or null if unsupported.</returns>
|
||||||
public virtual IConvertibleReplayFrame CreateConvertibleReplayFrame() => null;
|
public virtual IConvertibleReplayFrame CreateConvertibleReplayFrame() => null;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a ruleset info based on this ruleset.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A filled <see cref="RulesetInfo"/>.</returns>
|
|
||||||
private RulesetInfo createRulesetInfo() => new RulesetInfo
|
|
||||||
{
|
|
||||||
Name = Description,
|
|
||||||
ShortName = ShortName,
|
|
||||||
InstantiationInfo = GetType().AssemblyQualifiedName,
|
|
||||||
ID = LegacyID,
|
|
||||||
Available = true
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,17 @@ namespace osu.Game.Rulesets
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool Available { get; set; }
|
public bool Available { get; set; }
|
||||||
|
|
||||||
|
// TODO: this should probably be moved to RulesetStore.
|
||||||
public virtual Ruleset CreateInstance()
|
public virtual Ruleset CreateInstance()
|
||||||
{
|
{
|
||||||
if (!Available) return null;
|
if (!Available) return null;
|
||||||
|
|
||||||
return (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo));
|
var ruleset = (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo));
|
||||||
|
|
||||||
|
// overwrite the pre-populated RulesetInfo with a potentially database attached copy.
|
||||||
|
ruleset.RulesetInfo = this;
|
||||||
|
|
||||||
|
return ruleset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo;
|
public bool Equals(RulesetInfo other) => other != null && ID == other.ID && Available == other.Available && Name == other.Name && InstantiationInfo == other.InstantiationInfo;
|
||||||
|
|||||||
@@ -58,17 +58,17 @@ namespace osu.Game.Rulesets
|
|||||||
|
|
||||||
var instances = loadedAssemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r)).ToList();
|
var instances = loadedAssemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r)).ToList();
|
||||||
|
|
||||||
//add all legacy modes in correct order
|
//add all legacy rulesets first to ensure they have exclusive choice of primary key.
|
||||||
foreach (var r in instances.Where(r => r.LegacyID != null).OrderBy(r => r.LegacyID))
|
foreach (var r in instances.Where(r => r is ILegacyRuleset))
|
||||||
{
|
{
|
||||||
if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == r.RulesetInfo.ID) == null)
|
if (context.RulesetInfo.SingleOrDefault(dbRuleset => dbRuleset.ID == r.RulesetInfo.ID) == null)
|
||||||
context.RulesetInfo.Add(r.RulesetInfo);
|
context.RulesetInfo.Add(r.RulesetInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
|
|
||||||
//add any other modes
|
//add any other modes
|
||||||
foreach (var r in instances.Where(r => r.LegacyID == null))
|
foreach (var r in instances.Where(r => !(r is ILegacyRuleset)))
|
||||||
{
|
{
|
||||||
if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null)
|
if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null)
|
||||||
context.RulesetInfo.Add(r.RulesetInfo);
|
context.RulesetInfo.Add(r.RulesetInfo);
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Scoring
|
||||||
|
{
|
||||||
|
public class HealthProcessor : JudgementProcessor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when the <see cref="ScoreProcessor"/> is in a failed state.
|
||||||
|
/// Return true if the fail was permitted.
|
||||||
|
/// </summary>
|
||||||
|
public event Func<bool> Failed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional conditions on top of <see cref="DefaultFailCondition"/> that cause a failing state.
|
||||||
|
/// </summary>
|
||||||
|
public event Func<HealthProcessor, JudgementResult, bool> FailConditions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current health.
|
||||||
|
/// </summary>
|
||||||
|
public readonly BindableDouble Health = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this ScoreProcessor has already triggered the failed state.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasFailed { get; private set; }
|
||||||
|
|
||||||
|
public HealthProcessor(IBeatmap beatmap)
|
||||||
|
: base(beatmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyResultInternal(JudgementResult result)
|
||||||
|
{
|
||||||
|
result.HealthAtJudgement = Health.Value;
|
||||||
|
result.FailedAtJudgement = HasFailed;
|
||||||
|
|
||||||
|
if (HasFailed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Health.Value += HealthAdjustmentFactorFor(result) * result.Judgement.HealthIncreaseFor(result);
|
||||||
|
|
||||||
|
if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Failed?.Invoke() != false)
|
||||||
|
HasFailed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RevertResultInternal(JudgementResult result)
|
||||||
|
{
|
||||||
|
Health.Value = result.HealthAtJudgement;
|
||||||
|
|
||||||
|
// Todo: Revert HasFailed state with proper player support
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An adjustment factor which is multiplied into the health increase provided by a <see cref="JudgementResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">The <see cref="JudgementResult"/> for which the adjustment should apply.</param>
|
||||||
|
/// <returns>The adjustment factor.</returns>
|
||||||
|
protected virtual double HealthAdjustmentFactorFor(JudgementResult result) => 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default conditions for failing.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value);
|
||||||
|
|
||||||
|
protected override void Reset(bool storeResults)
|
||||||
|
{
|
||||||
|
base.Reset(storeResults);
|
||||||
|
|
||||||
|
Health.Value = 1;
|
||||||
|
HasFailed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Extensions.TypeExtensions;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Scoring
|
||||||
|
{
|
||||||
|
public abstract class JudgementProcessor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when all <see cref="HitObject"/>s have been judged by this <see cref="JudgementProcessor"/>.
|
||||||
|
/// </summary>
|
||||||
|
public event Action AllJudged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by this <see cref="JudgementProcessor"/>.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<JudgementResult> NewJudgement;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum number of hits that can be judged.
|
||||||
|
/// </summary>
|
||||||
|
protected int MaxHits { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total number of judged <see cref="HitObject"/>s at the current point in time.
|
||||||
|
/// </summary>
|
||||||
|
public int JudgedHits { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether all <see cref="Judgement"/>s have been processed.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasCompleted => JudgedHits == MaxHits;
|
||||||
|
|
||||||
|
protected JudgementProcessor(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
|
Reset(false);
|
||||||
|
SimulateAutoplay(beatmap);
|
||||||
|
Reset(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies any properties of the <see cref="IBeatmap"/> which affect scoring to this <see cref="ScoreProcessor"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The <see cref="IBeatmap"/> to read properties from.</param>
|
||||||
|
protected virtual void ApplyBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies the score change of a <see cref="JudgementResult"/> to this <see cref="ScoreProcessor"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">The <see cref="JudgementResult"/> to apply.</param>
|
||||||
|
public void ApplyResult(JudgementResult result)
|
||||||
|
{
|
||||||
|
JudgedHits++;
|
||||||
|
|
||||||
|
ApplyResultInternal(result);
|
||||||
|
|
||||||
|
NewJudgement?.Invoke(result);
|
||||||
|
|
||||||
|
if (HasCompleted)
|
||||||
|
AllJudged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">The judgement scoring result.</param>
|
||||||
|
public void RevertResult(JudgementResult result)
|
||||||
|
{
|
||||||
|
JudgedHits--;
|
||||||
|
|
||||||
|
RevertResultInternal(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies the score change of a <see cref="JudgementResult"/> to this <see cref="ScoreProcessor"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Any changes applied via this method can be reverted via <see cref="RevertResultInternal"/>.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="result">The <see cref="JudgementResult"/> to apply.</param>
|
||||||
|
protected abstract void ApplyResultInternal(JudgementResult result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/> via <see cref="ApplyResultInternal"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">The judgement scoring result.</param>
|
||||||
|
protected abstract void RevertResultInternal(JudgementResult result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets this <see cref="JudgementProcessor"/> to a default state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storeResults">Whether to store the current state of the <see cref="JudgementProcessor"/> for future use.</param>
|
||||||
|
protected virtual void Reset(bool storeResults)
|
||||||
|
{
|
||||||
|
if (storeResults)
|
||||||
|
MaxHits = JudgedHits;
|
||||||
|
|
||||||
|
JudgedHits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitObject">The <see cref="HitObject"/> which was judged.</param>
|
||||||
|
/// <param name="judgement">The <see cref="Judgement"/> that provides the scoring information.</param>
|
||||||
|
protected virtual JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new JudgementResult(hitObject, judgement);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simulates an autoplay of the <see cref="IBeatmap"/> to determine scoring values.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This provided temporarily. DO NOT USE.</remarks>
|
||||||
|
/// <param name="beatmap">The <see cref="IBeatmap"/> to simulate.</param>
|
||||||
|
protected virtual void SimulateAutoplay(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
foreach (var obj in beatmap.HitObjects)
|
||||||
|
simulate(obj);
|
||||||
|
|
||||||
|
void simulate(HitObject obj)
|
||||||
|
{
|
||||||
|
foreach (var nested in obj.NestedHitObjects)
|
||||||
|
simulate(nested);
|
||||||
|
|
||||||
|
var judgement = obj.CreateJudgement();
|
||||||
|
if (judgement == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var result = CreateResult(obj, judgement);
|
||||||
|
if (result == null)
|
||||||
|
throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
|
||||||
|
|
||||||
|
result.Type = judgement.MaxResult;
|
||||||
|
|
||||||
|
ApplyResult(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,44 +7,19 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.TypeExtensions;
|
|
||||||
using osu.Framework.MathUtils;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Scoring
|
namespace osu.Game.Rulesets.Scoring
|
||||||
{
|
{
|
||||||
public class ScoreProcessor
|
public class ScoreProcessor : JudgementProcessor
|
||||||
{
|
{
|
||||||
private const double base_portion = 0.3;
|
private const double base_portion = 0.3;
|
||||||
private const double combo_portion = 0.7;
|
private const double combo_portion = 0.7;
|
||||||
private const double max_score = 1000000;
|
private const double max_score = 1000000;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invoked when the <see cref="ScoreProcessor"/> is in a failed state.
|
|
||||||
/// This may occur regardless of whether an <see cref="AllJudged"/> event is invoked.
|
|
||||||
/// Return true if the fail was permitted.
|
|
||||||
/// </summary>
|
|
||||||
public event Func<bool> Failed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invoked when all <see cref="HitObject"/>s have been judged.
|
|
||||||
/// </summary>
|
|
||||||
public event Action AllJudged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the <see cref="ScoreProcessor"/>.
|
|
||||||
/// </summary>
|
|
||||||
public event Action<JudgementResult> NewJudgement;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional conditions on top of <see cref="DefaultFailCondition"/> that cause a failing state.
|
|
||||||
/// </summary>
|
|
||||||
public event Func<ScoreProcessor, JudgementResult, bool> FailConditions;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current total score.
|
/// The current total score.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -55,11 +30,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly BindableDouble Accuracy = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
public readonly BindableDouble Accuracy = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The current health.
|
|
||||||
/// </summary>
|
|
||||||
public readonly BindableDouble Health = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current combo.
|
/// The current combo.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -85,26 +55,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Bindable<ScoringMode> Mode = new Bindable<ScoringMode>();
|
public readonly Bindable<ScoringMode> Mode = new Bindable<ScoringMode>();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether all <see cref="Judgement"/>s have been processed.
|
|
||||||
/// </summary>
|
|
||||||
public bool HasCompleted => JudgedHits == MaxHits;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether this ScoreProcessor has already triggered the failed state.
|
|
||||||
/// </summary>
|
|
||||||
public bool HasFailed { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The maximum number of hits that can be judged.
|
|
||||||
/// </summary>
|
|
||||||
protected int MaxHits { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The total number of judged <see cref="HitObject"/>s at the current point in time.
|
|
||||||
/// </summary>
|
|
||||||
public int JudgedHits { get; private set; }
|
|
||||||
|
|
||||||
private double maxHighestCombo;
|
private double maxHighestCombo;
|
||||||
|
|
||||||
private double maxBaseScore;
|
private double maxBaseScore;
|
||||||
@@ -115,8 +65,14 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
private double scoreMultiplier = 1;
|
private double scoreMultiplier = 1;
|
||||||
|
|
||||||
public ScoreProcessor(IBeatmap beatmap)
|
public ScoreProcessor(IBeatmap beatmap)
|
||||||
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
Debug.Assert(base_portion + combo_portion == 1.0);
|
Debug.Assert(base_portion + combo_portion == 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
base.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue);
|
Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue);
|
||||||
Accuracy.ValueChanged += accuracy =>
|
Accuracy.ValueChanged += accuracy =>
|
||||||
@@ -126,12 +82,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue);
|
Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
ApplyBeatmap(beatmap);
|
|
||||||
|
|
||||||
Reset(false);
|
|
||||||
SimulateAutoplay(beatmap);
|
|
||||||
Reset(true);
|
|
||||||
|
|
||||||
if (maxBaseScore == 0 || maxHighestCombo == 0)
|
if (maxBaseScore == 0 || maxHighestCombo == 0)
|
||||||
{
|
{
|
||||||
Mode.Value = ScoringMode.Classic;
|
Mode.Value = ScoringMode.Classic;
|
||||||
@@ -150,91 +100,16 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies any properties of the <see cref="IBeatmap"/> which affect scoring to this <see cref="ScoreProcessor"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="beatmap">The <see cref="IBeatmap"/> to read properties from.</param>
|
|
||||||
protected virtual void ApplyBeatmap(IBeatmap beatmap)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Simulates an autoplay of the <see cref="IBeatmap"/> to determine scoring values.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This provided temporarily. DO NOT USE.</remarks>
|
|
||||||
/// <param name="beatmap">The <see cref="IBeatmap"/> to simulate.</param>
|
|
||||||
protected virtual void SimulateAutoplay(IBeatmap beatmap)
|
|
||||||
{
|
|
||||||
foreach (var obj in beatmap.HitObjects)
|
|
||||||
simulate(obj);
|
|
||||||
|
|
||||||
void simulate(HitObject obj)
|
|
||||||
{
|
|
||||||
foreach (var nested in obj.NestedHitObjects)
|
|
||||||
simulate(nested);
|
|
||||||
|
|
||||||
var judgement = obj.CreateJudgement();
|
|
||||||
if (judgement == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var result = CreateResult(obj, judgement);
|
|
||||||
if (result == null)
|
|
||||||
throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
|
|
||||||
|
|
||||||
result.Type = judgement.MaxResult;
|
|
||||||
|
|
||||||
ApplyResult(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies the score change of a <see cref="JudgementResult"/> to this <see cref="ScoreProcessor"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="result">The <see cref="JudgementResult"/> to apply.</param>
|
|
||||||
public void ApplyResult(JudgementResult result)
|
|
||||||
{
|
|
||||||
ApplyResultInternal(result);
|
|
||||||
|
|
||||||
updateScore();
|
|
||||||
updateFailed(result);
|
|
||||||
|
|
||||||
NewJudgement?.Invoke(result);
|
|
||||||
|
|
||||||
if (HasCompleted)
|
|
||||||
AllJudged?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="result">The judgement scoring result.</param>
|
|
||||||
public void RevertResult(JudgementResult result)
|
|
||||||
{
|
|
||||||
RevertResultInternal(result);
|
|
||||||
updateScore();
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
|
private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
|
||||||
|
|
||||||
/// <summary>
|
protected sealed override void ApplyResultInternal(JudgementResult result)
|
||||||
/// Applies the score change of a <see cref="JudgementResult"/> to this <see cref="ScoreProcessor"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Any changes applied via this method can be reverted via <see cref="RevertResultInternal"/>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="result">The <see cref="JudgementResult"/> to apply.</param>
|
|
||||||
protected virtual void ApplyResultInternal(JudgementResult result)
|
|
||||||
{
|
{
|
||||||
result.ComboAtJudgement = Combo.Value;
|
result.ComboAtJudgement = Combo.Value;
|
||||||
result.HighestComboAtJudgement = HighestCombo.Value;
|
result.HighestComboAtJudgement = HighestCombo.Value;
|
||||||
result.HealthAtJudgement = Health.Value;
|
|
||||||
result.FailedAtJudgement = HasFailed;
|
|
||||||
|
|
||||||
if (HasFailed)
|
if (result.FailedAtJudgement)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JudgedHits++;
|
|
||||||
|
|
||||||
if (result.Judgement.AffectsCombo)
|
if (result.Judgement.AffectsCombo)
|
||||||
{
|
{
|
||||||
switch (result.Type)
|
switch (result.Type)
|
||||||
@@ -266,26 +141,17 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
rollingMaxBaseScore += result.Judgement.MaxNumericResult;
|
rollingMaxBaseScore += result.Judgement.MaxNumericResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
Health.Value += HealthAdjustmentFactorFor(result) * result.Judgement.HealthIncreaseFor(result);
|
updateScore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
protected sealed override void RevertResultInternal(JudgementResult result)
|
||||||
/// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/> via <see cref="ApplyResultInternal"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="result">The judgement scoring result.</param>
|
|
||||||
protected virtual void RevertResultInternal(JudgementResult result)
|
|
||||||
{
|
{
|
||||||
Combo.Value = result.ComboAtJudgement;
|
Combo.Value = result.ComboAtJudgement;
|
||||||
HighestCombo.Value = result.HighestComboAtJudgement;
|
HighestCombo.Value = result.HighestComboAtJudgement;
|
||||||
Health.Value = result.HealthAtJudgement;
|
|
||||||
|
|
||||||
// Todo: Revert HasFailed state with proper player support
|
|
||||||
|
|
||||||
if (result.FailedAtJudgement)
|
if (result.FailedAtJudgement)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JudgedHits--;
|
|
||||||
|
|
||||||
if (result.Judgement.IsBonus)
|
if (result.Judgement.IsBonus)
|
||||||
{
|
{
|
||||||
if (result.IsHit)
|
if (result.IsHit)
|
||||||
@@ -299,14 +165,9 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
baseScore -= result.Judgement.NumericResultFor(result);
|
baseScore -= result.Judgement.NumericResultFor(result);
|
||||||
rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
|
rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
updateScore();
|
||||||
/// An adjustment factor which is multiplied into the health increase provided by a <see cref="JudgementResult"/>.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="result">The <see cref="JudgementResult"/> for which the adjustment should apply.</param>
|
|
||||||
/// <returns>The adjustment factor.</returns>
|
|
||||||
protected virtual double HealthAdjustmentFactorFor(JudgementResult result) => 1;
|
|
||||||
|
|
||||||
private void updateScore()
|
private void updateScore()
|
||||||
{
|
{
|
||||||
@@ -330,24 +191,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the score is in a failed state and notifies subscribers.
|
|
||||||
/// <para>
|
|
||||||
/// This can only ever notify subscribers once.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
private void updateFailed(JudgementResult result)
|
|
||||||
{
|
|
||||||
if (HasFailed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Failed?.Invoke() != false)
|
|
||||||
HasFailed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ScoreRank rankFrom(double acc)
|
private ScoreRank rankFrom(double acc)
|
||||||
{
|
{
|
||||||
if (acc == 1)
|
if (acc == 1)
|
||||||
@@ -372,30 +215,27 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// Resets this ScoreProcessor to a default state.
|
/// Resets this ScoreProcessor to a default state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="storeResults">Whether to store the current state of the <see cref="ScoreProcessor"/> for future use.</param>
|
/// <param name="storeResults">Whether to store the current state of the <see cref="ScoreProcessor"/> for future use.</param>
|
||||||
protected virtual void Reset(bool storeResults)
|
protected override void Reset(bool storeResults)
|
||||||
{
|
{
|
||||||
|
base.Reset(storeResults);
|
||||||
|
|
||||||
scoreResultCounts.Clear();
|
scoreResultCounts.Clear();
|
||||||
|
|
||||||
if (storeResults)
|
if (storeResults)
|
||||||
{
|
{
|
||||||
MaxHits = JudgedHits;
|
|
||||||
maxHighestCombo = HighestCombo.Value;
|
maxHighestCombo = HighestCombo.Value;
|
||||||
maxBaseScore = baseScore;
|
maxBaseScore = baseScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
JudgedHits = 0;
|
|
||||||
baseScore = 0;
|
baseScore = 0;
|
||||||
rollingMaxBaseScore = 0;
|
rollingMaxBaseScore = 0;
|
||||||
bonusScore = 0;
|
bonusScore = 0;
|
||||||
|
|
||||||
TotalScore.Value = 0;
|
TotalScore.Value = 0;
|
||||||
Accuracy.Value = 1;
|
Accuracy.Value = 1;
|
||||||
Health.Value = 1;
|
|
||||||
Combo.Value = 0;
|
Combo.Value = 0;
|
||||||
Rank.Value = ScoreRank.X;
|
Rank.Value = ScoreRank.X;
|
||||||
HighestCombo.Value = 0;
|
HighestCombo.Value = 0;
|
||||||
|
|
||||||
HasFailed = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -416,22 +256,10 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
score.Statistics[result] = GetStatistic(result);
|
score.Statistics[result] = GetStatistic(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The default conditions for failing.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a <see cref="HitWindows"/> for this processor.
|
/// Create a <see cref="HitWindows"/> for this processor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual HitWindows CreateHitWindows() => new HitWindows();
|
public virtual HitWindows CreateHitWindows() => new HitWindows();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hitObject">The <see cref="HitObject"/> which was judged.</param>
|
|
||||||
/// <param name="judgement">The <see cref="Judgement"/> that provides the scoring information.</param>
|
|
||||||
protected virtual JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new JudgementResult(hitObject, judgement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ScoringMode
|
public enum ScoringMode
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
dependencies.Cache(textureStore);
|
dependencies.Cache(textureStore);
|
||||||
|
|
||||||
localSampleStore = dependencies.Get<AudioManager>().GetSampleStore(new NamespacedResourceStore<byte[]>(resources, "Samples"));
|
localSampleStore = dependencies.Get<AudioManager>().GetSampleStore(new NamespacedResourceStore<byte[]>(resources, "Samples"));
|
||||||
dependencies.CacheAs(new FallbackSampleStore(localSampleStore, dependencies.Get<ISampleStore>()));
|
dependencies.CacheAs<ISampleStore>(new FallbackSampleStore(localSampleStore, dependencies.Get<ISampleStore>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
onScreenDisplay = dependencies.Get<OnScreenDisplay>();
|
onScreenDisplay = dependencies.Get<OnScreenDisplay>();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@@ -132,6 +133,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
private void confirmAndExit()
|
private void confirmAndExit()
|
||||||
{
|
{
|
||||||
|
if (exitConfirmed) return;
|
||||||
|
|
||||||
exitConfirmed = true;
|
exitConfirmed = true;
|
||||||
this.Exit();
|
this.Exit();
|
||||||
}
|
}
|
||||||
@@ -244,10 +247,18 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
public override bool OnExiting(IScreen next)
|
public override bool OnExiting(IScreen next)
|
||||||
{
|
{
|
||||||
if (!exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog))
|
if (!exitConfirmed && dialogOverlay != null)
|
||||||
{
|
{
|
||||||
dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort()));
|
if (dialogOverlay.CurrentDialog is ConfirmExitDialog exitDialog)
|
||||||
return true;
|
{
|
||||||
|
exitConfirmed = true;
|
||||||
|
exitDialog.Buttons.First().Click();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buttons.State = ButtonSystemState.Exit;
|
buttons.State = ButtonSystemState.Exit;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ namespace osu.Game.Screens.Play
|
|||||||
public Bindable<bool> ShowHealthbar = new Bindable<bool>(true);
|
public Bindable<bool> ShowHealthbar = new Bindable<bool>(true);
|
||||||
|
|
||||||
private readonly ScoreProcessor scoreProcessor;
|
private readonly ScoreProcessor scoreProcessor;
|
||||||
|
private readonly HealthProcessor healthProcessor;
|
||||||
private readonly DrawableRuleset drawableRuleset;
|
private readonly DrawableRuleset drawableRuleset;
|
||||||
private readonly IReadOnlyList<Mod> mods;
|
private readonly IReadOnlyList<Mod> mods;
|
||||||
|
|
||||||
@@ -63,9 +64,10 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private IEnumerable<Drawable> hideTargets => new Drawable[] { visibilityContainer, KeyCounter };
|
private IEnumerable<Drawable> hideTargets => new Drawable[] { visibilityContainer, KeyCounter };
|
||||||
|
|
||||||
public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
|
public HUDOverlay(ScoreProcessor scoreProcessor, HealthProcessor healthProcessor, DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
|
||||||
{
|
{
|
||||||
this.scoreProcessor = scoreProcessor;
|
this.scoreProcessor = scoreProcessor;
|
||||||
|
this.healthProcessor = healthProcessor;
|
||||||
this.drawableRuleset = drawableRuleset;
|
this.drawableRuleset = drawableRuleset;
|
||||||
this.mods = mods;
|
this.mods = mods;
|
||||||
|
|
||||||
@@ -119,7 +121,10 @@ namespace osu.Game.Screens.Play
|
|||||||
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay)
|
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay)
|
||||||
{
|
{
|
||||||
if (scoreProcessor != null)
|
if (scoreProcessor != null)
|
||||||
BindProcessor(scoreProcessor);
|
BindScoreProcessor(scoreProcessor);
|
||||||
|
|
||||||
|
if (healthProcessor != null)
|
||||||
|
BindHealthProcessor(healthProcessor);
|
||||||
|
|
||||||
if (drawableRuleset != null)
|
if (drawableRuleset != null)
|
||||||
{
|
{
|
||||||
@@ -288,15 +293,19 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay();
|
protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay();
|
||||||
|
|
||||||
protected virtual void BindProcessor(ScoreProcessor processor)
|
protected virtual void BindScoreProcessor(ScoreProcessor processor)
|
||||||
{
|
{
|
||||||
ScoreCounter?.Current.BindTo(processor.TotalScore);
|
ScoreCounter?.Current.BindTo(processor.TotalScore);
|
||||||
AccuracyCounter?.Current.BindTo(processor.Accuracy);
|
AccuracyCounter?.Current.BindTo(processor.Accuracy);
|
||||||
ComboCounter?.Current.BindTo(processor.Combo);
|
ComboCounter?.Current.BindTo(processor.Combo);
|
||||||
HealthDisplay?.Current.BindTo(processor.Health);
|
|
||||||
|
|
||||||
if (HealthDisplay is StandardHealthDisplay shd)
|
if (HealthDisplay is StandardHealthDisplay shd)
|
||||||
processor.NewJudgement += shd.Flash;
|
processor.NewJudgement += shd.Flash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void BindHealthProcessor(HealthProcessor processor)
|
||||||
|
{
|
||||||
|
HealthDisplay?.Current.BindTo(processor.Health);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ namespace osu.Game.Screens.Play
|
|||||||
public BreakOverlay BreakOverlay;
|
public BreakOverlay BreakOverlay;
|
||||||
|
|
||||||
protected ScoreProcessor ScoreProcessor { get; private set; }
|
protected ScoreProcessor ScoreProcessor { get; private set; }
|
||||||
|
|
||||||
|
protected HealthProcessor HealthProcessor { get; private set; }
|
||||||
|
|
||||||
protected DrawableRuleset DrawableRuleset { get; private set; }
|
protected DrawableRuleset DrawableRuleset { get; private set; }
|
||||||
|
|
||||||
protected HUDOverlay HUDOverlay { get; private set; }
|
protected HUDOverlay HUDOverlay { get; private set; }
|
||||||
@@ -131,6 +134,8 @@ namespace osu.Game.Screens.Play
|
|||||||
ScoreProcessor = ruleset.CreateScoreProcessor(playableBeatmap);
|
ScoreProcessor = ruleset.CreateScoreProcessor(playableBeatmap);
|
||||||
ScoreProcessor.Mods.BindTo(Mods);
|
ScoreProcessor.Mods.BindTo(Mods);
|
||||||
|
|
||||||
|
HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap);
|
||||||
|
|
||||||
if (!ScoreProcessor.Mode.Disabled)
|
if (!ScoreProcessor.Mode.Disabled)
|
||||||
config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
|
config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
|
||||||
|
|
||||||
@@ -145,15 +150,28 @@ namespace osu.Game.Screens.Play
|
|||||||
// bind clock into components that require it
|
// bind clock into components that require it
|
||||||
DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);
|
DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);
|
||||||
|
|
||||||
DrawableRuleset.OnNewResult += ScoreProcessor.ApplyResult;
|
DrawableRuleset.OnNewResult += r =>
|
||||||
DrawableRuleset.OnRevertResult += ScoreProcessor.RevertResult;
|
{
|
||||||
|
HealthProcessor.ApplyResult(r);
|
||||||
|
ScoreProcessor.ApplyResult(r);
|
||||||
|
};
|
||||||
|
|
||||||
// Bind ScoreProcessor to ourselves
|
DrawableRuleset.OnRevertResult += r =>
|
||||||
|
{
|
||||||
|
HealthProcessor.RevertResult(r);
|
||||||
|
ScoreProcessor.RevertResult(r);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bind the judgement processors to ourselves
|
||||||
ScoreProcessor.AllJudged += onCompletion;
|
ScoreProcessor.AllJudged += onCompletion;
|
||||||
ScoreProcessor.Failed += onFail;
|
HealthProcessor.Failed += onFail;
|
||||||
|
|
||||||
foreach (var mod in Mods.Value.OfType<IApplicableToScoreProcessor>())
|
foreach (var mod in Mods.Value.OfType<IApplicableToScoreProcessor>())
|
||||||
mod.ApplyToScoreProcessor(ScoreProcessor);
|
mod.ApplyToScoreProcessor(ScoreProcessor);
|
||||||
|
|
||||||
|
foreach (var mod in Mods.Value.OfType<IApplicableToHealthProcessor>())
|
||||||
|
mod.ApplyToHealthProcessor(HealthProcessor);
|
||||||
|
|
||||||
BreakOverlay.IsBreakTime.ValueChanged += _ => updatePauseOnFocusLostState();
|
BreakOverlay.IsBreakTime.ValueChanged += _ => updatePauseOnFocusLostState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +215,7 @@ namespace osu.Game.Screens.Play
|
|||||||
// display the cursor above some HUD elements.
|
// display the cursor above some HUD elements.
|
||||||
DrawableRuleset.Cursor?.CreateProxy() ?? new Container(),
|
DrawableRuleset.Cursor?.CreateProxy() ?? new Container(),
|
||||||
DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(),
|
DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(),
|
||||||
HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, Mods.Value)
|
HUDOverlay = new HUDOverlay(ScoreProcessor, HealthProcessor, DrawableRuleset, Mods.Value)
|
||||||
{
|
{
|
||||||
HoldToQuit =
|
HoldToQuit =
|
||||||
{
|
{
|
||||||
@@ -342,7 +360,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private void onCompletion()
|
private void onCompletion()
|
||||||
{
|
{
|
||||||
// Only show the completion screen if the player hasn't failed
|
// Only show the completion screen if the player hasn't failed
|
||||||
if (ScoreProcessor.HasFailed || completionProgressDelegate != null)
|
if (HealthProcessor.HasFailed || completionProgressDelegate != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ValidForResume = false;
|
ValidForResume = false;
|
||||||
@@ -350,18 +368,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (!showResults) return;
|
if (!showResults) return;
|
||||||
|
|
||||||
using (BeginDelayedSequence(1000))
|
using (BeginDelayedSequence(1000))
|
||||||
{
|
scheduleGotoRanking();
|
||||||
completionProgressDelegate = Schedule(delegate
|
|
||||||
{
|
|
||||||
if (!this.IsCurrentScreen()) return;
|
|
||||||
|
|
||||||
var score = CreateScore();
|
|
||||||
if (DrawableRuleset.ReplayScore == null)
|
|
||||||
scoreManager.Import(score).Wait();
|
|
||||||
|
|
||||||
this.Push(CreateResults(score));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual ScoreInfo CreateScore()
|
protected virtual ScoreInfo CreateScore()
|
||||||
@@ -542,7 +549,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (completionProgressDelegate != null && !completionProgressDelegate.Cancelled && !completionProgressDelegate.Completed)
|
if (completionProgressDelegate != null && !completionProgressDelegate.Cancelled && !completionProgressDelegate.Completed)
|
||||||
{
|
{
|
||||||
// proceed to result screen if beatmap already finished playing
|
// proceed to result screen if beatmap already finished playing
|
||||||
completionProgressDelegate.RunTask();
|
scheduleGotoRanking();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,7 +569,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
// GameplayClockContainer performs seeks / start / stop operations on the beatmap's track.
|
// GameplayClockContainer performs seeks / start / stop operations on the beatmap's track.
|
||||||
// as we are no longer the current screen, we cannot guarantee the track is still usable.
|
// as we are no longer the current screen, we cannot guarantee the track is still usable.
|
||||||
GameplayClockContainer.StopUsingBeatmapClock();
|
GameplayClockContainer?.StopUsingBeatmapClock();
|
||||||
|
|
||||||
fadeOut();
|
fadeOut();
|
||||||
return base.OnExiting(next);
|
return base.OnExiting(next);
|
||||||
@@ -577,6 +584,19 @@ namespace osu.Game.Screens.Play
|
|||||||
storyboardReplacesBackground.Value = false;
|
storyboardReplacesBackground.Value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scheduleGotoRanking()
|
||||||
|
{
|
||||||
|
completionProgressDelegate?.Cancel();
|
||||||
|
completionProgressDelegate = Schedule(delegate
|
||||||
|
{
|
||||||
|
var score = CreateScore();
|
||||||
|
if (DrawableRuleset.ReplayScore == null)
|
||||||
|
scoreManager.Import(score).Wait();
|
||||||
|
|
||||||
|
this.Push(CreateResults(score));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ namespace osu.Game.Screens.Select
|
|||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
IsSwitchable = true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
modsCheckbox = new OsuTabControlCheckbox
|
modsCheckbox = new OsuTabControlCheckbox
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ using osu.Framework.Bindables;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select.Details
|
namespace osu.Game.Screens.Select.Details
|
||||||
{
|
{
|
||||||
@@ -69,7 +72,37 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
mods.BindValueChanged(_ => updateStatistics(), true);
|
mods.BindValueChanged(modsChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<ISettingsItem> references = new List<ISettingsItem>();
|
||||||
|
|
||||||
|
private void modsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||||
|
{
|
||||||
|
// TODO: find a more permanent solution for this if/when it is needed in other components.
|
||||||
|
// this is generating drawables for the only purpose of storing bindable references.
|
||||||
|
foreach (var r in references)
|
||||||
|
r.Dispose();
|
||||||
|
|
||||||
|
references.Clear();
|
||||||
|
|
||||||
|
ScheduledDelegate debounce = null;
|
||||||
|
|
||||||
|
foreach (var mod in mods.NewValue.OfType<IApplicableToDifficulty>())
|
||||||
|
{
|
||||||
|
foreach (var setting in mod.CreateSettingsControls().OfType<ISettingsItem>())
|
||||||
|
{
|
||||||
|
setting.SettingChanged += () =>
|
||||||
|
{
|
||||||
|
debounce?.Cancel();
|
||||||
|
debounce = Scheduler.AddDelayed(updateStatistics, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
references.Add(setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStatistics()
|
private void updateStatistics()
|
||||||
|
|||||||
@@ -13,13 +13,12 @@ namespace osu.Game.Skinning
|
|||||||
: base(Info, storage, audioManager, string.Empty)
|
: base(Info, storage, audioManager, string.Empty)
|
||||||
{
|
{
|
||||||
Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255);
|
Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255);
|
||||||
Configuration.ComboColours.AddRange(new[]
|
Configuration.AddComboColours(
|
||||||
{
|
|
||||||
new Color4(255, 192, 0, 255),
|
new Color4(255, 192, 0, 255),
|
||||||
new Color4(0, 202, 0, 255),
|
new Color4(0, 202, 0, 255),
|
||||||
new Color4(18, 124, 255, 255),
|
new Color4(18, 124, 255, 255),
|
||||||
new Color4(242, 24, 57, 255),
|
new Color4(242, 24, 57, 255)
|
||||||
});
|
);
|
||||||
|
|
||||||
Configuration.LegacyVersion = 2.0m;
|
Configuration.LegacyVersion = 2.0m;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace osu.Game.Skinning
|
|||||||
switch (global)
|
switch (global)
|
||||||
{
|
{
|
||||||
case GlobalSkinConfiguration.ComboColours:
|
case GlobalSkinConfiguration.ComboColours:
|
||||||
return SkinUtils.As<TValue>(new Bindable<List<Color4>>(Configuration.ComboColours));
|
return SkinUtils.As<TValue>(new Bindable<IReadOnlyList<Color4>>(Configuration.ComboColours));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -10,15 +8,5 @@ namespace osu.Game.Skinning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DefaultSkinConfiguration : SkinConfiguration
|
public class DefaultSkinConfiguration : SkinConfiguration
|
||||||
{
|
{
|
||||||
public DefaultSkinConfiguration()
|
|
||||||
{
|
|
||||||
ComboColours.AddRange(new[]
|
|
||||||
{
|
|
||||||
new Color4(255, 192, 0, 255),
|
|
||||||
new Color4(0, 202, 0, 255),
|
|
||||||
new Color4(18, 124, 255, 255),
|
|
||||||
new Color4(242, 24, 57, 255),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ namespace osu.Game.Skinning
|
|||||||
public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore<byte[]> storage, AudioManager audioManager)
|
public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore<byte[]> storage, AudioManager audioManager)
|
||||||
: base(createSkinInfo(beatmap), new LegacySkinResourceStore<BeatmapSetFileInfo>(beatmap.BeatmapSet, storage), audioManager, beatmap.Path)
|
: base(createSkinInfo(beatmap), new LegacySkinResourceStore<BeatmapSetFileInfo>(beatmap.BeatmapSet, storage), audioManager, beatmap.Path)
|
||||||
{
|
{
|
||||||
|
// Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer)
|
||||||
|
Configuration.AllowDefaultComboColoursFallback = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SkinInfo createSkinInfo(BeatmapInfo beatmap) =>
|
private static SkinInfo createSkinInfo(BeatmapInfo beatmap) =>
|
||||||
|
|||||||
@@ -72,7 +72,11 @@ namespace osu.Game.Skinning
|
|||||||
switch (global)
|
switch (global)
|
||||||
{
|
{
|
||||||
case GlobalSkinConfiguration.ComboColours:
|
case GlobalSkinConfiguration.ComboColours:
|
||||||
return SkinUtils.As<TValue>(new Bindable<List<Color4>>(Configuration.ComboColours));
|
var comboColours = Configuration.ComboColours;
|
||||||
|
if (comboColours != null)
|
||||||
|
return SkinUtils.As<TValue>(new Bindable<IReadOnlyList<Color4>>(comboColours));
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
public class LegacySkinConfiguration : DefaultSkinConfiguration
|
public class LegacySkinConfiguration : SkinConfiguration
|
||||||
{
|
{
|
||||||
public const decimal LATEST_VERSION = 2.7m;
|
public const decimal LATEST_VERSION = 2.7m;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,36 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
public readonly SkinInfo SkinInfo = new SkinInfo();
|
public readonly SkinInfo SkinInfo = new SkinInfo();
|
||||||
|
|
||||||
public List<Color4> ComboColours { get; set; } = new List<Color4>();
|
/// <summary>
|
||||||
|
/// Whether to allow <see cref="DefaultComboColours"/> as a fallback list for when no combo colours are provided.
|
||||||
|
/// </summary>
|
||||||
|
internal bool AllowDefaultComboColoursFallback = true;
|
||||||
|
|
||||||
|
public static List<Color4> DefaultComboColours { get; } = new List<Color4>
|
||||||
|
{
|
||||||
|
new Color4(255, 192, 0, 255),
|
||||||
|
new Color4(0, 202, 0, 255),
|
||||||
|
new Color4(18, 124, 255, 255),
|
||||||
|
new Color4(242, 24, 57, 255),
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly List<Color4> comboColours = new List<Color4>();
|
||||||
|
|
||||||
|
public IReadOnlyList<Color4> ComboColours
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (comboColours.Count > 0)
|
||||||
|
return comboColours;
|
||||||
|
|
||||||
|
if (AllowDefaultComboColoursFallback)
|
||||||
|
return DefaultComboColours;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddComboColours(params Color4[] colours) => comboColours.AddRange(colours);
|
||||||
|
|
||||||
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
|
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2019.1219.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2019.1225.0" />
|
||||||
<PackageReference Include="Sentry" Version="1.2.0" />
|
<PackageReference Include="Sentry" Version="1.2.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
|
|||||||
+2
-2
@@ -74,7 +74,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1219.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1225.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2019.1219.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2019.1225.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
|||||||
Reference in New Issue
Block a user