1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-15 15:12:57 +08:00

Merge branch 'master' into drawnode-composability

This commit is contained in:
Dean Herbert 2019-04-27 18:41:19 +09:00 committed by GitHub
commit d40177f97c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
144 changed files with 1303 additions and 626 deletions

View File

@ -1,6 +1,6 @@
clone_depth: 1 clone_depth: 1
version: '{branch}-{build}' version: '{branch}-{build}'
image: Visual Studio 2017 image: Previous Visual Studio 2017
test: off test: off
install: install:
- cmd: git submodule update --init --recursive --depth=5 - cmd: git submodule update --init --recursive --depth=5

View File

@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Tests
protected override Player CreatePlayer(Ruleset ruleset) protected override Player CreatePlayer(Ruleset ruleset)
{ {
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return base.CreatePlayer(ruleset); return base.CreatePlayer(ruleset);
} }
} }

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch
{ {
public class CatchRuleset : Ruleset public class CatchRuleset : Ruleset
{ {
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableCatchRuleset(this, beatmap); public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableCatchRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);

View File

@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
default: default:
return 0; return 0;
case HitResult.Perfect: case HitResult.Perfect:
return 8; return 0.008;
} }
} }

View File

@ -23,9 +23,9 @@ namespace osu.Game.Rulesets.Catch.Judgements
switch (result) switch (result)
{ {
default: default:
return 0; return base.HealthIncreaseFor(result);
case HitResult.Perfect: case HitResult.Perfect:
return 7; return 0.007;
} }
} }
} }

View File

@ -27,9 +27,9 @@ namespace osu.Game.Rulesets.Catch.Judgements
switch (result) switch (result)
{ {
default: default:
return 0; return -0.02;
case HitResult.Perfect: case HitResult.Perfect:
return 10.2; return 0.01;
} }
} }

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
default: default:
return 0; return 0;
case HitResult.Perfect: case HitResult.Perfect:
return 4; return 0.004;
} }
} }
} }

View File

@ -1,7 +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 System;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
@ -27,20 +26,15 @@ namespace osu.Game.Rulesets.Catch.Scoring
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
} }
private const double harshness = 0.01; protected override double HealthAdjustmentFactorFor(JudgementResult result)
protected override void ApplyResult(JudgementResult result)
{ {
base.ApplyResult(result); switch (result.Type)
if (result.Type == HitResult.Miss)
{ {
if (!result.Judgement.IsBonus) case HitResult.Miss:
Health.Value -= hpDrainRate * (harshness * 2); return hpDrainRate;
return; default:
return 10.2 - hpDrainRate; // Award less HP as drain rate is increased
} }
Health.Value += Math.Max(result.Judgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
} }
public override HitWindows CreateHitWindows() => new CatchHitWindows(); public override HitWindows CreateHitWindows() => new CatchHitWindows();

View File

@ -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.Collections.Generic;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
@ -10,6 +11,7 @@ using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
@ -23,8 +25,8 @@ namespace osu.Game.Rulesets.Catch.UI
protected override bool UserScrollSpeedAdjustment => false; protected override bool UserScrollSpeedAdjustment => false;
public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap) public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap) : base(ruleset, beatmap, mods)
{ {
Direction.Value = ScrollingDirection.Down; Direction.Value = ScrollingDirection.Down;
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);

View File

@ -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.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -8,6 +10,7 @@ using osu.Framework.Timing;
using osu.Game.Rulesets.Mania.Edit; using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
@ -21,6 +24,9 @@ namespace osu.Game.Rulesets.Mania.Tests
{ {
private readonly Column column; private readonly Column column;
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
protected ManiaPlacementBlueprintTestCase() protected ManiaPlacementBlueprintTestCase()
{ {
Add(column = new Column(0) Add(column = new Column(0)

View File

@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.Mania.UI.Components;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using osuTK; using osuTK;
@ -31,6 +32,9 @@ namespace osu.Game.Rulesets.Mania.Tests
typeof(ColumnHitObjectArea) typeof(ColumnHitObjectArea)
}; };
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
private readonly List<Column> columns = new List<Column>(); private readonly List<Column> columns = new List<Column>();
public TestCaseColumn() public TestCaseColumn()

View File

@ -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 System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
@ -13,6 +14,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using osuTK; using osuTK;
@ -24,6 +26,9 @@ namespace osu.Game.Rulesets.Mania.Tests
{ {
private const int columns = 4; private const int columns = 4;
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
private readonly List<ManiaStage> stages = new List<ManiaStage>(); private readonly List<ManiaStage> stages = new List<ManiaStage>();
private FillFlowContainer<ScrollingTestContainer> fill; private FillFlowContainer<ScrollingTestContainer> fill;

View File

@ -1,10 +1,12 @@
// 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.Collections.Generic;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osuTK; using osuTK;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
@ -14,8 +16,8 @@ namespace osu.Game.Rulesets.Mania.Edit
{ {
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo; public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap) public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap) : base(ruleset, beatmap, mods)
{ {
} }

View File

@ -11,6 +11,7 @@ using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components;
using osuTK; using osuTK;
@ -41,9 +42,9 @@ namespace osu.Game.Rulesets.Mania.Edit
public int TotalColumns => ((ManiaPlayfield)DrawableRuleset.Playfield).TotalColumns; public int TotalColumns => ((ManiaPlayfield)DrawableRuleset.Playfield).TotalColumns;
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap) protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
{ {
DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap); DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
// This is the earliest we can cache the scrolling info to ourselves, before masks are added to the hierarchy and inject it // This is the earliest we can cache the scrolling info to ourselves, before masks are added to the hierarchy and inject it
dependencies.CacheAs(DrawableRuleset.ScrollingInfo); dependencies.CacheAs(DrawableRuleset.ScrollingInfo);

View File

@ -10,5 +10,16 @@ namespace osu.Game.Rulesets.Mania.Judgements
public override bool AffectsCombo => false; public override bool AffectsCombo => false;
protected override int NumericResultFor(HitResult result) => 20; protected override int NumericResultFor(HitResult result) => 20;
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
case HitResult.Miss:
return 0;
default:
return 0.040;
}
}
} }
} }

View File

@ -25,5 +25,26 @@ namespace osu.Game.Rulesets.Mania.Judgements
return 300; return 300;
} }
} }
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
case HitResult.Miss:
return -0.125;
case HitResult.Meh:
return 0.005;
case HitResult.Ok:
return 0.010;
case HitResult.Good:
return 0.035;
case HitResult.Great:
return 0.055;
case HitResult.Perfect:
return 0.065;
default:
return 0;
}
}
} }
} }

View File

@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Mania
{ {
public class ManiaRuleset : Ruleset public class ManiaRuleset : Ruleset
{ {
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableManiaRuleset(this, beatmap); public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableManiaRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
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);

View File

@ -3,7 +3,6 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -28,36 +27,6 @@ namespace osu.Game.Rulesets.Mania.Scoring
/// </summary> /// </summary>
private const double hp_multiplier_max = 1; private const double hp_multiplier_max = 1;
/// <summary>
/// The default BAD hit HP increase.
/// </summary>
private const double hp_increase_bad = 0.005;
/// <summary>
/// The default OK hit HP increase.
/// </summary>
private const double hp_increase_ok = 0.010;
/// <summary>
/// The default GOOD hit HP increase.
/// </summary>
private const double hp_increase_good = 0.035;
/// <summary>
/// The default tick hit HP increase.
/// </summary>
private const double hp_increase_tick = 0.040;
/// <summary>
/// The default GREAT hit HP increase.
/// </summary>
private const double hp_increase_great = 0.055;
/// <summary>
/// The default PERFECT hit HP increase.
/// </summary>
private const double hp_increase_perfect = 0.065;
/// <summary> /// <summary>
/// The MISS HP multiplier at OD = 0. /// The MISS HP multiplier at OD = 0.
/// </summary> /// </summary>
@ -73,11 +42,6 @@ namespace osu.Game.Rulesets.Mania.Scoring
/// </summary> /// </summary>
private const double hp_multiplier_miss_max = 1; private const double hp_multiplier_miss_max = 1;
/// <summary>
/// The default MISS HP increase.
/// </summary>
private const double hp_increase_miss = -0.125;
/// <summary> /// <summary>
/// The MISS HP multiplier. This is multiplied to the miss hp increase. /// The MISS HP multiplier. This is multiplied to the miss hp increase.
/// </summary> /// </summary>
@ -88,10 +52,6 @@ namespace osu.Game.Rulesets.Mania.Scoring
/// </summary> /// </summary>
private double hpMultiplier = 1; private double hpMultiplier = 1;
public ManiaScoreProcessor()
{
}
public ManiaScoreProcessor(DrawableRuleset<ManiaHitObject> drawableRuleset) public ManiaScoreProcessor(DrawableRuleset<ManiaHitObject> drawableRuleset)
: base(drawableRuleset) : base(drawableRuleset)
{ {
@ -122,42 +82,8 @@ namespace osu.Game.Rulesets.Mania.Scoring
} }
} }
protected override void ApplyResult(JudgementResult result) protected override double HealthAdjustmentFactorFor(JudgementResult result)
{ => result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier;
base.ApplyResult(result);
bool isTick = result.Judgement is HoldNoteTickJudgement;
if (isTick)
{
if (result.IsHit)
Health.Value += hpMultiplier * hp_increase_tick;
}
else
{
switch (result.Type)
{
case HitResult.Miss:
Health.Value += hpMissMultiplier * hp_increase_miss;
break;
case HitResult.Meh:
Health.Value += hpMultiplier * hp_increase_bad;
break;
case HitResult.Ok:
Health.Value += hpMultiplier * hp_increase_ok;
break;
case HitResult.Good:
Health.Value += hpMultiplier * hp_increase_good;
break;
case HitResult.Great:
Health.Value += hpMultiplier * hp_increase_great;
break;
case HitResult.Perfect:
Health.Value += hpMultiplier * hp_increase_perfect;
break;
}
}
}
public override HitWindows CreateHitWindows() => new ManiaHitWindows(); public override HitWindows CreateHitWindows() => new ManiaHitWindows();
} }

View File

@ -18,6 +18,7 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -39,8 +40,8 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>(); private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap) public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap) : base(ruleset, beatmap, mods)
{ {
// Generate the bar lines // Generate the bar lines
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;

View File

@ -1,11 +1,13 @@
// 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.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using Decoder = osu.Game.Beatmaps.Formats.Decoder; using Decoder = osu.Game.Beatmaps.Formats.Decoder;
@ -22,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Tests
using (var reader = new StreamReader(stream)) using (var reader = new StreamReader(stream))
{ {
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader); var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo); var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>());
var objects = converted.HitObjects.ToList(); var objects = converted.HitObjects.ToList();

View File

@ -30,7 +30,6 @@ namespace osu.Game.Rulesets.Osu.Tests
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private int depthIndex; private int depthIndex;
protected readonly List<Mod> Mods = new List<Mod>();
public TestCaseHitCircle() public TestCaseHitCircle()
{ {
@ -68,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Depth = depthIndex++ Depth = depthIndex++
}; };
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>()) foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
mod.ApplyToDrawableHitObjects(new[] { drawable }); mod.ApplyToDrawableHitObjects(new[] { drawable });
Add(drawable); Add(drawable);

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public TestCaseHitCircleHidden() public TestCaseHitCircleHidden()
{ {
Mods.Add(new OsuModHidden()); Mods.Value = new[] { new OsuModHidden() };
} }
} }
} }

View File

@ -44,7 +44,6 @@ namespace osu.Game.Rulesets.Osu.Tests
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private int depthIndex; private int depthIndex;
protected readonly List<Mod> Mods = new List<Mod>();
public TestCaseSlider() public TestCaseSlider()
{ {
@ -292,7 +291,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Depth = depthIndex++ Depth = depthIndex++
}; };
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>()) foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
mod.ApplyToDrawableHitObjects(new[] { drawable }); mod.ApplyToDrawableHitObjects(new[] { drawable });
drawable.OnNewResult += onNewResult; drawable.OnNewResult += onNewResult;

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public TestCaseSliderHidden() public TestCaseSliderHidden()
{ {
Mods.Add(new OsuModHidden()); Mods.Value = new[] { new OsuModHidden() };
} }
} }
} }

View File

@ -31,7 +31,6 @@ namespace osu.Game.Rulesets.Osu.Tests
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private int depthIndex; private int depthIndex;
protected readonly List<Mod> Mods = new List<Mod>();
public TestCaseSpinner() public TestCaseSpinner()
{ {
@ -57,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Depth = depthIndex++ Depth = depthIndex++
}; };
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>()) foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
mod.ApplyToDrawableHitObjects(new[] { drawable }); mod.ApplyToDrawableHitObjects(new[] { drawable });
Add(drawable); Add(drawable);

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public TestCaseSpinnerHidden() public TestCaseSpinnerHidden()
{ {
Mods.Add(new OsuModHidden()); Mods.Value = new[] { new OsuModHidden() };
} }
} }
} }

View File

@ -1,7 +1,9 @@
// 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.Collections.Generic;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osuTK; using osuTK;
@ -10,8 +12,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{ {
public class DrawableOsuEditRuleset : DrawableOsuRuleset public class DrawableOsuEditRuleset : DrawableOsuRuleset
{ {
public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap) public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap) : base(ruleset, beatmap, mods)
{ {
} }

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
@ -23,8 +24,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{ {
} }
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap) protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new DrawableOsuEditRuleset(ruleset, beatmap); => new DrawableOsuEditRuleset(ruleset, beatmap, mods);
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[] protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
{ {

View File

@ -24,5 +24,20 @@ namespace osu.Game.Rulesets.Osu.Judgements
return 300; return 300;
} }
} }
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
case HitResult.Miss:
return -0.02;
case HitResult.Meh:
case HitResult.Good:
case HitResult.Great:
return 0.01;
default:
return 0;
}
}
} }
} }

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu
{ {
public class OsuRuleset : Ruleset public class OsuRuleset : Ruleset
{ {
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableOsuRuleset(this, beatmap); public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableOsuRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);

View File

@ -37,8 +37,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
comboResultCounts.Clear(); comboResultCounts.Clear();
} }
private const double harshness = 0.01;
protected override void ApplyResult(JudgementResult result) protected override void ApplyResult(JudgementResult result)
{ {
base.ApplyResult(result); base.ApplyResult(result);
@ -47,28 +45,29 @@ namespace osu.Game.Rulesets.Osu.Scoring
if (result.Type != HitResult.None) if (result.Type != HitResult.None)
comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1; comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1;
}
protected override double HealthAdjustmentFactorFor(JudgementResult result)
{
switch (result.Type) switch (result.Type)
{ {
case HitResult.Great: case HitResult.Great:
Health.Value += (10.2 - hpDrainRate) * harshness; return 10.2 - hpDrainRate;
break;
case HitResult.Good: case HitResult.Good:
Health.Value += (8 - hpDrainRate) * harshness; return 8 - hpDrainRate;
break;
case HitResult.Meh: case HitResult.Meh:
Health.Value += (4 - hpDrainRate) * harshness; return 4 - hpDrainRate;
break;
/*case HitResult.SliderTick: // case HitResult.SliderTick:
Health.Value += Math.Max(7 - hpDrainRate, 0) * 0.01; // return Math.Max(7 - hpDrainRate, 0) * 0.01;
break;*/
case HitResult.Miss: case HitResult.Miss:
Health.Value -= hpDrainRate * (harshness * 2); return hpDrainRate;
break;
default:
return 0;
} }
} }

View File

@ -1,11 +1,13 @@
// 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.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Input.Handlers; using osu.Game.Input.Handlers;
using osu.Game.Replays; using osu.Game.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
@ -22,8 +24,8 @@ namespace osu.Game.Rulesets.Osu.UI
{ {
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config; protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
public DrawableOsuRuleset(Ruleset ruleset, WorkingBeatmap beatmap) public DrawableOsuRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap) : base(ruleset, beatmap, mods)
{ {
} }

View File

@ -11,6 +11,7 @@ using osu.Framework.MathUtils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
@ -86,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Origin = Anchor.Centre, Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 768, Height = 768,
Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap) } Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap, Array.Empty<Mod>()) }
}); });
} }

View File

@ -46,19 +46,8 @@ namespace osu.Game.Rulesets.Taiko.Scoring
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
} }
protected override void ApplyResult(JudgementResult result) protected override double HealthAdjustmentFactorFor(JudgementResult result)
{ => result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier;
base.ApplyResult(result);
double hpIncrease = result.Judgement.HealthIncreaseFor(result);
if (result.Type == HitResult.Miss)
hpIncrease *= hpMissMultiplier;
else
hpIncrease *= hpMultiplier;
Health.Value += hpIncrease;
}
protected override void Reset(bool storeResults) protected override void Reset(bool storeResults)
{ {

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko
{ {
public class TaikoRuleset : Ruleset public class TaikoRuleset : Ruleset
{ {
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableTaikoRuleset(this, beatmap); public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableTaikoRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[] public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]

View File

@ -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.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
@ -16,6 +17,7 @@ using osu.Framework.Input;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Input.Handlers; using osu.Game.Input.Handlers;
using osu.Game.Replays; using osu.Game.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Taiko.UI namespace osu.Game.Rulesets.Taiko.UI
@ -26,8 +28,8 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override bool UserScrollSpeedAdjustment => false; protected override bool UserScrollSpeedAdjustment => false;
public DrawableTaikoRuleset(Ruleset ruleset, WorkingBeatmap beatmap) public DrawableTaikoRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap) : base(ruleset, beatmap, mods)
{ {
Direction.Value = ScrollingDirection.Left; Direction.Value = ScrollingDirection.Left;
TimeRange.Value = 7000; TimeRange.Value = 7000;

View File

@ -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.IO; using System.IO;
using NUnit.Framework; using NUnit.Framework;
using osuTK; using osuTK;
@ -13,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.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
@ -39,7 +41,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(6, working.BeatmapInfo.BeatmapVersion); Assert.AreEqual(6, working.BeatmapInfo.BeatmapVersion);
Assert.AreEqual(6, working.Beatmap.BeatmapInfo.BeatmapVersion); Assert.AreEqual(6, working.Beatmap.BeatmapInfo.BeatmapVersion);
Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapVersion); Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>()).BeatmapInfo.BeatmapVersion);
} }
} }

View File

@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual.Background
AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null); AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null);
AddStep("Set default user settings", () => AddStep("Set default user settings", () =>
{ {
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { new OsuModNoFail() }); Mods.Value = Mods.Value.Concat(new[] { new OsuModNoFail() }).ToArray();
songSelect.DimLevel.Value = 0.7f; songSelect.DimLevel.Value = 0.7f;
songSelect.BlurLevel.Value = 0.4f; songSelect.BlurLevel.Value = 0.4f;
}); });

View File

@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
protected override Player CreatePlayer(Ruleset ruleset) protected override Player CreatePlayer(Ruleset ruleset)
{ {
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new ScoreAccessiblePlayer(); return new ScoreAccessiblePlayer();
} }

View File

@ -0,0 +1,150 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.UI;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestCaseFrameStabilityContainer : OsuTestCase
{
private readonly ManualClock manualClock;
private readonly Container mainContainer;
private ClockConsumingChild consumer;
public TestCaseFrameStabilityContainer()
{
Child = mainContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Clock = new FramedClock(manualClock = new ManualClock()),
};
}
[Test]
public void TestLargeJumps()
{
seekManualTo(0);
createStabilityContainer();
seekManualTo(100000);
confirmSeek(100000);
checkFrameCount(6000);
seekManualTo(0);
confirmSeek(0);
checkFrameCount(12000);
}
[Test]
public void TestSmallJumps()
{
seekManualTo(0);
createStabilityContainer();
seekManualTo(40);
confirmSeek(40);
checkFrameCount(3);
seekManualTo(0);
confirmSeek(0);
checkFrameCount(6);
}
[Test]
public void TestSingleFrameJump()
{
seekManualTo(0);
createStabilityContainer();
seekManualTo(8);
confirmSeek(8);
checkFrameCount(1);
seekManualTo(16);
confirmSeek(16);
checkFrameCount(2);
}
[Test]
public void TestInitialSeek()
{
seekManualTo(100000);
createStabilityContainer();
confirmSeek(100000);
checkFrameCount(0);
}
private void createStabilityContainer() => AddStep("create container", () => mainContainer.Child = new FrameStabilityContainer().WithChild(consumer = new ClockConsumingChild()));
private void seekManualTo(double time) => AddStep($"seek manual clock to {time}", () => manualClock.CurrentTime = time);
private void confirmSeek(double time) => AddUntilStep($"wait for seek to {time}", () => consumer.Clock.CurrentTime == time);
private void checkFrameCount(int frames) =>
AddAssert($"elapsed frames is {frames}", () => consumer.ElapsedFrames == frames);
public class ClockConsumingChild : CompositeDrawable
{
private readonly OsuSpriteText text;
private readonly OsuSpriteText text2;
private readonly OsuSpriteText text3;
public ClockConsumingChild()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
text = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
text2 = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
text3 = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
}
},
};
}
public int ElapsedFrames;
protected override void Update()
{
base.Update();
if (Clock.ElapsedFrameTime != 0)
ElapsedFrames++;
text.Text = $"current time: {Clock.CurrentTime:F0}";
if (Clock.ElapsedFrameTime != 0)
text2.Text = $"last elapsed frame time: {Clock.ElapsedFrameTime:F0}";
text3.Text = $"total frames: {ElapsedFrames:F0}";
}
}
}
}

View File

@ -1,43 +1,42 @@
// 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.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens; using osu.Game.Screens;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestCasePlayerLoader : ManualInputManagerTestCase public class TestCasePlayerLoader : ManualInputManagerTestCase
{ {
private PlayerLoader loader; private PlayerLoader loader;
private readonly OsuScreenStack stack; private OsuScreenStack stack;
public TestCasePlayerLoader() [SetUp]
public void Setup() => Schedule(() =>
{ {
InputManager.Add(stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }); InputManager.Child = stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
} Beatmap.Value = new TestWorkingBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), Clock);
});
[BackgroundDependencyLoader] [Test]
private void load(OsuGameBase game) public void TestLoadContinuation()
{ {
Beatmap.Value = new DummyWorkingBeatmap(game);
AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player(false, false)))); AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player(false, false))));
AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddUntilStep("wait for current", () => loader.IsCurrentScreen());
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen()); AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen());
AddStep("exit loader", () => loader.Exit());
AddUntilStep("wait for no longer alive", () => !loader.IsAlive);
AddStep("load slow dummy beatmap", () => AddStep("load slow dummy beatmap", () =>
{ {
SlowLoadPlayer slow = null; SlowLoadPlayer slow = null;
@ -50,6 +49,65 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen()); AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen());
} }
[Test]
public void TestModReinstantiation()
{
TestPlayer player = null;
TestMod gameMod = null;
TestMod playerMod1 = null;
TestMod playerMod2 = null;
AddStep("load player", () =>
{
Mods.Value = new[] { gameMod = new TestMod() };
stack.Push(loader = new PlayerLoader(() => player = new TestPlayer()));
});
AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen());
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for player to be current", () => player.IsCurrentScreen());
AddStep("retrieve mods", () => playerMod1 = (TestMod)player.Mods.Value.Single());
AddAssert("game mods not applied", () => gameMod.Applied == false);
AddAssert("player mods applied", () => playerMod1.Applied);
AddStep("restart player", () =>
{
var lastPlayer = player;
player = null;
lastPlayer.Restart();
});
AddUntilStep("wait for player to be current", () => player.IsCurrentScreen());
AddStep("retrieve mods", () => playerMod2 = (TestMod)player.Mods.Value.Single());
AddAssert("game mods not applied", () => gameMod.Applied == false);
AddAssert("player has different mods", () => playerMod1 != playerMod2);
AddAssert("player mods applied", () => playerMod2.Applied);
}
private class TestMod : Mod, IApplicableToScoreProcessor
{
public override string Name => string.Empty;
public override string Acronym => string.Empty;
public override double ScoreMultiplier => 1;
public bool Applied { get; private set; }
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{
Applied = true;
}
}
private class TestPlayer : Player
{
public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods;
public TestPlayer()
: base(false, false)
{
}
}
protected class SlowLoadPlayer : Player protected class SlowLoadPlayer : Player
{ {
public bool Ready; public bool Ready;

View File

@ -1,9 +1,11 @@
// 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.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
@ -15,7 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
protected override Player CreatePlayer(Ruleset ruleset) protected override Player CreatePlayer(Ruleset ruleset)
{ {
var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo); var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty<Mod>());
return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap)); return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap));
} }

View File

@ -4,11 +4,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Timing; using osu.Game.Rulesets.Timing;
@ -23,6 +25,9 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Playfield) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Playfield) };
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
private readonly ScrollingTestContainer[] scrollContainers = new ScrollingTestContainer[4]; private readonly ScrollingTestContainer[] scrollContainers = new ScrollingTestContainer[4];
private readonly TestPlayfield[] playfields = new TestPlayfield[4]; private readonly TestPlayfield[] playfields = new TestPlayfield[4];

View File

@ -120,12 +120,8 @@ namespace osu.Game.Tests.Visual.Gameplay
} }
} }
private class SecondarySource : ISkinSource private class SecondarySource : ISkin
{ {
public event Action SourceChanged;
public void TriggerSourceChanged() => SourceChanged?.Invoke();
public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox(); public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox();
public Texture GetTexture(string componentName) => throw new NotImplementedException(); public Texture GetTexture(string componentName) => throw new NotImplementedException();
@ -135,12 +131,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException(); public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
} }
private class SkinSourceContainer : Container, ISkinSource private class SkinSourceContainer : Container, ISkin
{ {
public event Action SourceChanged;
public void TriggerSourceChanged() => SourceChanged?.Invoke();
public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox(); public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox();
public Texture GetTexture(string componentName) => throw new NotImplementedException(); public Texture GetTexture(string componentName) => throw new NotImplementedException();

View File

@ -23,7 +23,11 @@ namespace osu.Game.Tests.Visual.Menus
public TestCaseLoaderAnimation() public TestCaseLoaderAnimation()
{ {
Child = logo = new OsuLogo { Depth = float.MinValue }; Child = logo = new OsuLogo
{
Alpha = 0,
Depth = float.MinValue
};
} }
[Test] [Test]
@ -39,7 +43,7 @@ namespace osu.Game.Tests.Visual.Menus
LoadScreen(loader); LoadScreen(loader);
}); });
AddAssert("loaded", () => AddUntilStep("loaded", () =>
{ {
logoVisible = loader.Logo?.Alpha > 0; logoVisible = loader.Logo?.Alpha > 0;
return loader.Logo != null && loader.ScreenLoaded; return loader.Logo != null && loader.ScreenLoaded;

View File

@ -35,10 +35,6 @@ namespace osu.Game.Tests.Visual.SongSelect
private WorkingBeatmap defaultBeatmap; private WorkingBeatmap defaultBeatmap;
private DatabaseContextFactory factory; private DatabaseContextFactory factory;
[Cached]
[Cached(Type = typeof(IBindable<IEnumerable<Mod>>))]
private readonly Bindable<IEnumerable<Mod>> selectedMods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
typeof(Screens.Select.SongSelect), typeof(Screens.Select.SongSelect),
@ -175,19 +171,19 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("change ruleset", () => AddStep("change ruleset", () =>
{ {
songSelect.CurrentBeatmap.Mods.ValueChanged += onModChange; Mods.ValueChanged += onModChange;
songSelect.Ruleset.ValueChanged += onRulesetChange; songSelect.Ruleset.ValueChanged += onRulesetChange;
Ruleset.Value = new TaikoRuleset().RulesetInfo; Ruleset.Value = new TaikoRuleset().RulesetInfo;
songSelect.CurrentBeatmap.Mods.ValueChanged -= onModChange; Mods.ValueChanged -= onModChange;
songSelect.Ruleset.ValueChanged -= onRulesetChange; songSelect.Ruleset.ValueChanged -= onRulesetChange;
}); });
AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex); AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex);
AddAssert("empty mods", () => !selectedMods.Value.Any()); AddAssert("empty mods", () => !Mods.Value.Any());
void onModChange(ValueChangedEvent<IEnumerable<Mod>> e) => modChangeIndex = actionIndex++; void onModChange(ValueChangedEvent<IReadOnlyList<Mod>> e) => modChangeIndex = actionIndex++;
void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex--; void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex--;
} }
@ -218,7 +214,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private static int importId; private static int importId;
private int getImportId() => ++importId; private int getImportId() => ++importId;
private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => selectedMods.Value = mods); private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => Mods.Value = mods);
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id)); private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));

View File

@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.UserInterface
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
buttons = new ButtonSystem(), buttons = new ButtonSystem(),
logo = new OsuLogo() logo = new OsuLogo { RelativePositionAxes = Axes.Both }
}; };
buttons.SetOsuLogo(logo); buttons.SetOsuLogo(logo);

View File

@ -0,0 +1,299 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.MathUtils;
using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestCaseLogoTrackingContainer : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(PlayerLoader),
typeof(Player),
typeof(LogoTrackingContainer),
typeof(ButtonSystem),
typeof(ButtonSystemState),
typeof(Menu),
typeof(MainMenu)
};
private OsuLogo logo;
private TestLogoTrackingContainer trackingContainer;
private Container transferContainer;
private Box visualBox;
private Box transferContainerBox;
private Drawable logoFacade;
private bool randomPositions;
private const float visual_box_size = 72;
[SetUpSteps]
public void SetUpSteps()
{
AddStep("Clear facades", () =>
{
Clear();
Add(logo = new OsuLogo { Scale = new Vector2(0.15f), RelativePositionAxes = Axes.Both });
trackingContainer = null;
transferContainer = null;
});
}
/// <summary>
/// Move the facade to 0,0, then move it to a random new location while the logo is still transforming to it.
/// Check if the logo is still tracking the facade.
/// </summary>
[Test]
public void TestMoveFacade()
{
AddToggleStep("Toggle move continuously", b => randomPositions = b);
AddStep("Add tracking containers", addFacadeContainers);
AddStep("Move facade to random position", moveLogoFacade);
waitForMove();
AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking);
}
/// <summary>
/// Check if the facade is removed from the container, the logo stops tracking.
/// </summary>
[Test]
public void TestRemoveFacade()
{
AddStep("Add tracking containers", addFacadeContainers);
AddStep("Move facade to random position", moveLogoFacade);
AddStep("Remove facade from FacadeContainer", removeFacade);
waitForMove();
AddAssert("Logo is not tracking", () => !trackingContainer.IsLogoTracking);
}
/// <summary>
/// Check if the facade gets added to a new container, tracking starts on the new facade.
/// </summary>
[Test]
public void TestTransferFacade()
{
AddStep("Add tracking containers", addFacadeContainers);
AddStep("Move facade to random position", moveLogoFacade);
AddStep("Remove facade from FacadeContainer", removeFacade);
AddStep("Transfer facade to a new container", () =>
{
transferContainer.Add(logoFacade);
transferContainerBox.Colour = Color4.Tomato;
moveLogoFacade();
});
waitForMove();
AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking);
}
/// <summary>
/// Add a facade to a flow container, move the logo to the center of the screen, then start tracking on the facade.
/// </summary>
[Test]
public void TestFlowContainer()
{
FillFlowContainer flowContainer;
AddStep("Create new flow container with facade", () =>
{
Add(trackingContainer = new TestLogoTrackingContainer
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Child = flowContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Direction = FillDirection.Vertical,
}
});
flowContainer.Children = new Drawable[]
{
new Box
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Colour = Color4.Azure,
Size = new Vector2(visual_box_size)
},
new Container
{
Alpha = 0.35f,
RelativeSizeAxes = Axes.None,
Size = new Vector2(visual_box_size),
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Children = new Drawable[]
{
visualBox = new Box
{
Colour = Color4.White,
RelativeSizeAxes = Axes.Both,
},
trackingContainer.LogoFacade,
}
},
new Box
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Colour = Color4.Azure,
Size = new Vector2(visual_box_size)
},
};
});
AddStep("Perform logo movements", () =>
{
trackingContainer.StopTracking();
logo.MoveTo(new Vector2(0.5f), 500, Easing.InOutExpo);
visualBox.Colour = Color4.White;
Scheduler.AddDelayed(() =>
{
trackingContainer.StartTracking(logo, 1000, Easing.InOutExpo);
visualBox.Colour = Color4.Tomato;
}, 700);
});
waitForMove(8);
AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking);
}
[Test]
public void TestSetFacadeSize()
{
bool failed = false;
AddStep("Set up scenario", () =>
{
failed = false;
addFacadeContainers();
});
AddStep("Try setting facade size", () =>
{
try
{
logoFacade.Size = new Vector2(0, 0);
}
catch (Exception e)
{
if (e is InvalidOperationException)
failed = true;
}
});
AddAssert("Exception thrown", () => failed);
}
[Test]
public void TestSetMultipleContainers()
{
bool failed = false;
LogoTrackingContainer newContainer = new LogoTrackingContainer();
AddStep("Set up scenario", () =>
{
failed = false;
newContainer = new LogoTrackingContainer();
addFacadeContainers();
moveLogoFacade();
});
AddStep("Try tracking new container", () =>
{
try
{
newContainer.StartTracking(logo);
}
catch (Exception e)
{
if (e is InvalidOperationException)
failed = true;
}
});
AddAssert("Exception thrown", () => failed);
}
private void addFacadeContainers()
{
AddRange(new Drawable[]
{
trackingContainer = new TestLogoTrackingContainer
{
Alpha = 0.35f,
RelativeSizeAxes = Axes.None,
Size = new Vector2(visual_box_size),
Child = visualBox = new Box
{
Colour = Color4.Tomato,
RelativeSizeAxes = Axes.Both,
}
},
transferContainer = new Container
{
Alpha = 0.35f,
RelativeSizeAxes = Axes.None,
Size = new Vector2(visual_box_size),
Child = transferContainerBox = new Box
{
Colour = Color4.White,
RelativeSizeAxes = Axes.Both,
}
},
});
trackingContainer.Add(logoFacade = trackingContainer.LogoFacade);
trackingContainer.StartTracking(logo, 1000);
}
private void waitForMove(int count = 5) => AddWaitStep("Wait for transforms to finish", count);
private void removeFacade()
{
trackingContainer.Remove(logoFacade);
visualBox.Colour = Color4.White;
moveLogoFacade();
}
private void moveLogoFacade()
{
if (logoFacade?.Transforms.Count == 0 && transferContainer?.Transforms.Count == 0)
{
Random random = new Random();
trackingContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300);
transferContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300);
}
if (randomPositions)
Schedule(moveLogoFacade);
}
private class TestLogoTrackingContainer : LogoTrackingContainer
{
/// <summary>
/// Check that the logo is tracking the position of the facade, with an acceptable precision lenience.
/// </summary>
public bool IsLogoTracking => Precision.AlmostEquals(Logo.Position, ComputeLogoTrackingPosition());
}
}
}

View File

@ -253,7 +253,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private class TestModSelectOverlay : ModSelectOverlay private class TestModSelectOverlay : ModSelectOverlay
{ {
public new Bindable<IEnumerable<Mod>> SelectedMods => base.SelectedMods; public new Bindable<IReadOnlyList<Mod>> SelectedMods => base.SelectedMods;
public ModButton GetModButton(Mod mod) public ModButton GetModButton(Mod mod)
{ {

View File

@ -4,7 +4,7 @@
namespace osu.Game.Audio namespace osu.Game.Audio
{ {
/// <summary> /// <summary>
/// Interface for objects that can own <see cref="IPreviewTrack"/>s. /// Interface for objects that can own <see cref="PreviewTrack"/>s.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <see cref="IPreviewTrackOwner"/>s can cancel the currently playing <see cref="PreviewTrack"/> through the /// <see cref="IPreviewTrackOwner"/>s can cancel the currently playing <see cref="PreviewTrack"/> through the

View File

@ -98,7 +98,7 @@ namespace osu.Game.Beatmaps
protected abstract IEnumerable<Type> ValidConversionTypes { get; } protected abstract IEnumerable<Type> ValidConversionTypes { get; }
/// <summary> /// <summary>
/// Creates the <see cref="Beatmap{T}"/> that will be returned by this <see cref="BeatmapProcessor{T}"/>. /// Creates the <see cref="Beatmap{T}"/> that will be returned by this <see cref="BeatmapProcessor"/>.
/// </summary> /// </summary>
protected virtual Beatmap<T> CreateBeatmap() => new Beatmap<T>(); protected virtual Beatmap<T> CreateBeatmap() => new Beatmap<T>();

View File

@ -217,7 +217,7 @@ namespace osu.Game.Beatmaps
{ {
request.Perform(api); request.Perform(api);
} }
catch (Exception e) catch
{ {
// no need to handle here as exceptions will filter down to request.Failure above. // no need to handle here as exceptions will filter down to request.Failure above.
} }
@ -382,7 +382,6 @@ namespace osu.Game.Beatmaps
/// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status. /// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status.
/// </summary> /// </summary>
/// <param name="beatmap">The beatmap to populate.</param> /// <param name="beatmap">The beatmap to populate.</param>
/// <param name="otherBeatmaps">The other beatmaps contained within this set.</param>
/// <param name="force">Whether to re-query if the provided beatmap already has populated values.</param> /// <param name="force">Whether to re-query if the provided beatmap already has populated values.</param>
/// <returns>True if population was successful.</returns> /// <returns>True if population was successful.</returns>
private bool fetchAndPopulateOnlineValues(BeatmapInfo beatmap, bool force = false) private bool fetchAndPopulateOnlineValues(BeatmapInfo beatmap, bool force = false)

View File

@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps
{ {
/// <summary> /// <summary>
/// A <see cref="Bindable{T}"/> for the <see cref="OsuGame"/> beatmap. /// A <see cref="Bindable{T}"/> for the <see cref="OsuGame"/> beatmap.
/// This should be used sparingly in-favour of <see cref="IBindable<WorkingBeatmap>"/>. /// This should be used sparingly in-favour of <see cref="IBindable{WorkingBeatmap}"/>.
/// </summary> /// </summary>
public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap> public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap>
{ {
@ -67,6 +67,6 @@ namespace osu.Game.Beatmaps
/// If you are further binding to events of the retrieved <see cref="BindableBeatmap"/>, ensure a local reference is held. /// If you are further binding to events of the retrieved <see cref="BindableBeatmap"/>, ensure a local reference is held.
/// </summary> /// </summary>
[NotNull] [NotNull]
public abstract BindableBeatmap GetBoundCopy(); public new abstract BindableBeatmap GetBoundCopy();
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty;
@ -52,7 +53,7 @@ namespace osu.Game.Beatmaps
{ {
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { }; public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { };
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -73,9 +74,18 @@ namespace osu.Game.Beatmaps
private class DummyBeatmapConverter : IBeatmapConverter private class DummyBeatmapConverter : IBeatmapConverter
{ {
public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted; public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
public IBeatmap Beatmap { get; set; } public IBeatmap Beatmap { get; set; }
public bool CanConvert => true; public bool CanConvert => true;
public IBeatmap Convert() => Beatmap;
public IBeatmap Convert()
{
foreach (var obj in Beatmap.HitObjects)
ObjectConverted?.Invoke(obj, obj.Yield());
return Beatmap;
}
} }
} }
} }

View File

@ -95,7 +95,7 @@ namespace osu.Game.Beatmaps.Formats
{ {
colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), split.Length == 4 ? byte.Parse(split[3]) : (byte)255); colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), split.Length == 4 ? byte.Parse(split[3]) : (byte)255);
} }
catch (Exception e) catch
{ {
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
} }

View File

@ -11,7 +11,6 @@ using osu.Framework.IO.File;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using osu.Framework.Bindables;
using osu.Game.IO.Serialization; using osu.Game.IO.Serialization;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -28,16 +27,12 @@ namespace osu.Game.Beatmaps
public readonly BeatmapMetadata Metadata; public readonly BeatmapMetadata Metadata;
public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
protected WorkingBeatmap(BeatmapInfo beatmapInfo) protected WorkingBeatmap(BeatmapInfo beatmapInfo)
{ {
BeatmapInfo = beatmapInfo; BeatmapInfo = beatmapInfo;
BeatmapSetInfo = beatmapInfo.BeatmapSet; BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
Mods.ValueChanged += _ => applyRateAdjustments();
beatmap = new RecyclableLazy<IBeatmap>(() => beatmap = new RecyclableLazy<IBeatmap>(() =>
{ {
var b = GetBeatmap() ?? new Beatmap(); var b = GetBeatmap() ?? new Beatmap();
@ -51,14 +46,7 @@ namespace osu.Game.Beatmaps
return b; return b;
}); });
track = new RecyclableLazy<Track>(() => track = new RecyclableLazy<Track>(() => GetTrack() ?? new VirtualBeatmapTrack(Beatmap));
{
// we want to ensure that we always have a track, even if it's a fake one.
var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap);
applyRateAdjustments(t);
return t;
});
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid); background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
waveform = new RecyclableLazy<Waveform>(GetWaveform); waveform = new RecyclableLazy<Waveform>(GetWaveform);
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard); storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
@ -85,9 +73,10 @@ namespace osu.Game.Beatmaps
/// </para> /// </para>
/// </summary> /// </summary>
/// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param> /// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param>
/// <param name="mods">The <see cref="Mod"/>s to apply to the <see cref="IBeatmap"/>.</param>
/// <returns>The converted <see cref="IBeatmap"/>.</returns> /// <returns>The converted <see cref="IBeatmap"/>.</returns>
/// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception> /// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception>
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset) public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods)
{ {
var rulesetInstance = ruleset.CreateInstance(); var rulesetInstance = ruleset.CreateInstance();
@ -98,19 +87,19 @@ namespace osu.Game.Beatmaps
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter})."); throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
// Apply conversion mods // Apply conversion mods
foreach (var mod in Mods.Value.OfType<IApplicableToBeatmapConverter>()) foreach (var mod in mods.OfType<IApplicableToBeatmapConverter>())
mod.ApplyToBeatmapConverter(converter); mod.ApplyToBeatmapConverter(converter);
// Convert // Convert
IBeatmap converted = converter.Convert(); IBeatmap converted = converter.Convert();
// Apply difficulty mods // Apply difficulty mods
if (Mods.Value.Any(m => m is IApplicableToDifficulty)) if (mods.Any(m => m is IApplicableToDifficulty))
{ {
converted.BeatmapInfo = converted.BeatmapInfo.Clone(); converted.BeatmapInfo = converted.BeatmapInfo.Clone();
converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone(); converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone();
foreach (var mod in Mods.Value.OfType<IApplicableToDifficulty>()) foreach (var mod in mods.OfType<IApplicableToDifficulty>())
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty); mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
} }
@ -122,7 +111,7 @@ namespace osu.Game.Beatmaps
foreach (var obj in converted.HitObjects) foreach (var obj in converted.HitObjects)
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty); obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
foreach (var mod in Mods.Value.OfType<IApplicableToHitObject>()) foreach (var mod in mods.OfType<IApplicableToHitObject>())
foreach (var obj in converted.HitObjects) foreach (var obj in converted.HitObjects)
mod.ApplyToHitObject(obj); mod.ApplyToHitObject(obj);
@ -188,16 +177,6 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public void RecycleTrack() => track.Recycle(); public void RecycleTrack() => track.Recycle();
private void applyRateAdjustments(Track t = null)
{
if (t == null && track.IsResultAvailable) t = Track;
if (t == null) return;
t.ResetSpeedAdjustments();
foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(t);
}
public class RecyclableLazy<T> public class RecyclableLazy<T>
{ {
private Lazy<T> lazy; private Lazy<T> lazy;

View File

@ -563,7 +563,7 @@ namespace osu.Game.Database
/// <summary> /// <summary>
/// Check whether an existing model already exists for a new import item. /// Check whether an existing model already exists for a new import item.
/// </summary> /// </summary>
/// <param name="model">The new model proposed for import. /// <param name="model">The new model proposed for import.</param>
/// <returns>An existing model which matches the criteria to skip importing, else null.</returns> /// <returns>An existing model which matches the criteria to skip importing, else null.</returns>
protected TModel CheckForExisting(TModel model) => model.Hash == null ? null : ModelStore.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash); protected TModel CheckForExisting(TModel model) => model.Hash == null ? null : ModelStore.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash);

View File

@ -1,7 +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 System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
@ -67,7 +66,7 @@ namespace osu.Game.Database
context = threadContexts.Value; context = threadContexts.Value;
} }
} }
catch (Exception e) catch
{ {
// retrieval of a context could trigger a fatal error. // retrieval of a context could trigger a fatal error.
Monitor.Exit(writeLock); Monitor.Exit(writeLock);

View File

@ -70,7 +70,7 @@ namespace osu.Game.Database
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
} }
catch (Exception e) catch
{ {
connection.Close(); connection.Close();
throw; throw;

View File

@ -103,7 +103,7 @@ namespace osu.Game.Graphics.Containers
{ {
channelManager?.OpenChannel(linkArgument); channelManager?.OpenChannel(linkArgument);
} }
catch (ChannelNotFoundException e) catch (ChannelNotFoundException)
{ {
Logger.Log($"The requested channel \"{linkArgument}\" does not exist"); Logger.Log($"The requested channel \"{linkArgument}\" does not exist");
} }

View File

@ -0,0 +1,157 @@
// 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;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using osu.Game.Screens.Menu;
using osuTK;
namespace osu.Game.Graphics.Containers
{
/// <summary>
/// A container that handles tracking of an <see cref="OsuLogo"/> through different layout scenarios.
/// </summary>
public class LogoTrackingContainer : Container
{
public Facade LogoFacade => facade;
protected OsuLogo Logo { get; private set; }
private readonly InternalFacade facade = new InternalFacade();
private Easing easing;
private Vector2? startPosition;
private double? startTime;
private double duration;
/// <summary>
/// Assign the logo that should track the facade's position, as well as how it should transform to its initial position.
/// </summary>
/// <param name="logo">The instance of the logo to be used for tracking.</param>
/// <param name="duration">The duration of the initial transform. Default is instant.</param>
/// <param name="easing">The easing type of the initial transform.</param>
public void StartTracking(OsuLogo logo, double duration = 0, Easing easing = Easing.None)
{
if (logo == null)
throw new ArgumentNullException(nameof(logo));
if (logo.IsTracking && Logo == null)
throw new InvalidOperationException($"Cannot track an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s");
if (Logo != logo && Logo != null)
{
// If we're replacing the logo to be tracked, the old one no longer has a tracking container
Logo.IsTracking = false;
}
Logo = logo;
Logo.IsTracking = true;
this.duration = duration;
this.easing = easing;
startTime = null;
startPosition = null;
}
/// <summary>
/// Stops the logo assigned in <see cref="StartTracking"/> from tracking the facade's position.
/// </summary>
public void StopTracking()
{
if (Logo != null)
{
Logo.IsTracking = false;
Logo = null;
}
}
/// <summary>
/// Gets the position that the logo should move to with respect to the <see cref="LogoFacade"/>.
/// Manually performs a conversion of the Facade's position to the Logo's parent's relative space.
/// </summary>
/// <remarks>Will only be correct if the logo's <see cref="Drawable.RelativePositionAxes"/> are set to Axes.Both</remarks>
protected Vector2 ComputeLogoTrackingPosition()
{
var absolutePos = Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre);
return new Vector2(absolutePos.X / Logo.Parent.RelativeToAbsoluteFactor.X,
absolutePos.Y / Logo.Parent.RelativeToAbsoluteFactor.Y);
}
protected override void Update()
{
base.Update();
if (Logo == null)
return;
if (Logo.RelativePositionAxes != Axes.Both)
throw new InvalidOperationException($"Tracking logo must have {nameof(RelativePositionAxes)} = Axes.Both");
// Account for the scale of the actual OsuLogo, as SizeForFlow only accounts for the sprite scale.
facade.SetSize(new Vector2(Logo.SizeForFlow * Logo.Scale.X));
var localPos = ComputeLogoTrackingPosition();
if (LogoFacade.Parent != null && Logo.Position != localPos)
{
// If this is our first update since tracking has started, initialize our starting values for interpolation
if (startTime == null || startPosition == null)
{
startTime = Time.Current;
startPosition = Logo.Position;
}
if (duration != 0)
{
double elapsedDuration = (double)(Time.Current - startTime);
var amount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1));
// Interpolate the position of the logo, where amount 0 is where the logo was when it first began interpolating, and amount 1 is the target location.
Logo.Position = Vector2.Lerp(startPosition.Value, localPos, amount);
}
else
{
Logo.Position = localPos;
}
}
}
protected override void Dispose(bool isDisposing)
{
if (Logo != null)
Logo.IsTracking = false;
base.Dispose(isDisposing);
}
private class InternalFacade : Facade
{
public new void SetSize(Vector2 size)
{
base.SetSize(size);
}
}
/// <summary>
/// A dummy object used to denote another object's location.
/// </summary>
public abstract class Facade : Drawable
{
public override Vector2 Size
{
get => base.Size;
set => throw new InvalidOperationException($"Cannot set the Size of a {typeof(Facade)} outside of a {typeof(LogoTrackingContainer)}");
}
protected void SetSize(Vector2 size)
{
base.Size = size;
}
}
}
}

View File

@ -61,9 +61,9 @@ namespace osu.Game.Graphics
/// <summary> /// <summary>
/// Retrieves the string representation of a <see cref="FontWeight"/>. /// Retrieves the string representation of a <see cref="FontWeight"/>.
/// </summary> /// </summary>
/// <param name="typeface">The <see cref="Typeface"/>.</param> /// <param name="family">The family string.</param>
/// <param name="weight">The <see cref="FontWeight"/>.</param> /// <param name="weight">The <see cref="FontWeight"/>.</param>
/// <returns>The string representation of <paramref name="weight"/> in the specified <paramref name="typeface"/>.</returns> /// <returns>The string representation of <paramref name="weight"/> in the specified <paramref name="family"/>.</returns>
public static string GetWeightString(string family, FontWeight weight) public static string GetWeightString(string family, FontWeight weight)
{ {
string weightString = weight.ToString(); string weightString = weight.ToString();
@ -81,6 +81,7 @@ namespace osu.Game.Graphics
/// <summary> /// <summary>
/// Creates a new <see cref="FontUsage"/> by applying adjustments to this <see cref="FontUsage"/>. /// Creates a new <see cref="FontUsage"/> by applying adjustments to this <see cref="FontUsage"/>.
/// </summary> /// </summary>
/// <param name="usage">The base <see cref="FontUsage"/>.</param>
/// <param name="typeface">The font typeface. If null, the value is copied from this <see cref="FontUsage"/>.</param> /// <param name="typeface">The font typeface. If null, the value is copied from this <see cref="FontUsage"/>.</param>
/// <param name="size">The text size. If null, the value is copied from this <see cref="FontUsage"/>.</param> /// <param name="size">The text size. If null, the value is copied from this <see cref="FontUsage"/>.</param>
/// <param name="weight">The font weight. If null, the value is copied from this <see cref="FontUsage"/>.</param> /// <param name="weight">The font weight. If null, the value is copied from this <see cref="FontUsage"/>.</param>

View File

@ -8,7 +8,7 @@ using osu.Framework.Screens;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
{ {
/// <summary> /// <summary>
/// A <see cref="BreadcrumbControl"/> which follows the active screen (and allows navigation) in a <see cref="Screen"/> stack. /// A <see cref="BreadcrumbControl{IScreen}"/> which follows the active screen (and allows navigation) in a <see cref="Screen"/> stack.
/// </summary> /// </summary>
public class ScreenBreadcrumbControl : BreadcrumbControl<IScreen> public class ScreenBreadcrumbControl : BreadcrumbControl<IScreen>
{ {

View File

@ -253,7 +253,7 @@ namespace osu.Game.Online.API
handleWebException(we); handleWebException(we);
return false; return false;
} }
catch (Exception e) catch
{ {
return false; return false;
} }

View File

@ -7,7 +7,6 @@ using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring.Legacy; using osu.Game.Scoring.Legacy;
using osu.Game.Users; using osu.Game.Users;
@ -71,7 +70,6 @@ namespace osu.Game.Online.API.Requests.Responses
{ {
foreach (var kvp in value) foreach (var kvp in value)
{ {
HitResult newKey;
switch (kvp.Key) switch (kvp.Key)
{ {
case @"count_geki": case @"count_geki":

View File

@ -10,16 +10,16 @@ namespace osu.Game.Online.API.Requests.Responses
public class APIUserMostPlayedBeatmap public class APIUserMostPlayedBeatmap
{ {
[JsonProperty("beatmap_id")] [JsonProperty("beatmap_id")]
public int BeatmapID; public int BeatmapID { get; set; }
[JsonProperty("count")] [JsonProperty("count")]
public int PlayCount; public int PlayCount { get; set; }
[JsonProperty] [JsonProperty]
private BeatmapInfo beatmap; private BeatmapInfo beatmap { get; set; }
[JsonProperty] [JsonProperty]
private APIBeatmapSet beatmapSet; private APIBeatmapSet beatmapSet { get; set; }
public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets)
{ {

View File

@ -27,8 +27,6 @@ namespace osu.Game.Online.Chat
protected ChannelManager ChannelManager; protected ChannelManager ChannelManager;
private ScrollContainer scroll;
private DrawableChannel drawableChannel; private DrawableChannel drawableChannel;
private readonly bool postingTextbox; private readonly bool postingTextbox;

View File

@ -112,8 +112,8 @@ namespace osu.Game
// todo: move this to SongSelect once Screen has the ability to unsuspend. // todo: move this to SongSelect once Screen has the ability to unsuspend.
[Cached] [Cached]
[Cached(Type = typeof(IBindable<IEnumerable<Mod>>))] [Cached(typeof(IBindable<IReadOnlyList<Mod>>))]
private readonly Bindable<IEnumerable<Mod>> selectedMods = new Bindable<IEnumerable<Mod>>(new Mod[] { }); private readonly Bindable<IReadOnlyList<Mod>> mods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
public OsuGame(string[] args = null) public OsuGame(string[] args = null)
{ {
@ -272,7 +272,6 @@ namespace osu.Game
/// Present a score's replay immediately. /// Present a score's replay immediately.
/// The user should have already requested this interactively. /// The user should have already requested this interactively.
/// </summary> /// </summary>
/// <param name="beatmap">The beatmap to select.</param>
public void PresentScore(ScoreInfo score) public void PresentScore(ScoreInfo score)
{ {
var databasedScore = ScoreManager.GetScore(score); var databasedScore = ScoreManager.GetScore(score);
@ -293,9 +292,8 @@ namespace osu.Game
performFromMainMenu(() => performFromMainMenu(() =>
{ {
ruleset.Value = databasedScoreInfo.Ruleset; ruleset.Value = databasedScoreInfo.Ruleset;
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
Beatmap.Value.Mods.Value = databasedScoreInfo.Mods; mods.Value = databasedScoreInfo.Mods;
menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore))); menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore)));
}, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true); }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);

View File

@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Chat.Tabs
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private new void load(OsuColour colour) private void load(OsuColour colour)
{ {
BackgroundInactive = colour.Gray2; BackgroundInactive = colour.Gray2;
BackgroundActive = colour.Gray3; BackgroundActive = colour.Gray3;

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
@ -18,9 +17,6 @@ namespace osu.Game.Overlays.Chat.Tabs
{ {
public class PrivateChannelTabItem : ChannelTabItem public class PrivateChannelTabItem : ChannelTabItem
{ {
private readonly OsuSpriteText username;
private readonly Avatar avatarContainer;
protected override IconUsage DisplayIcon => FontAwesome.Solid.At; protected override IconUsage DisplayIcon => FontAwesome.Solid.At;
public PrivateChannelTabItem(Channel value) public PrivateChannelTabItem(Channel value)

View File

@ -11,7 +11,7 @@ namespace osu.Game.Overlays
{ {
/// <summary> /// <summary>
/// An overlay which will display a black screen that dims over a period before confirming an exit action. /// An overlay which will display a black screen that dims over a period before confirming an exit action.
/// Action is BYO (derived class will need to call <see cref="BeginConfirm"/> and <see cref="AbortConfirm"/> from a user event). /// Action is BYO (derived class will need to call <see cref="HoldToConfirmContainer.BeginConfirm"/> and <see cref="HoldToConfirmContainer.AbortConfirm"/> from a user event).
/// </summary> /// </summary>
public abstract class HoldToConfirmOverlay : HoldToConfirmContainer public abstract class HoldToConfirmOverlay : HoldToConfirmContainer
{ {

View File

@ -42,19 +42,19 @@ namespace osu.Game.Overlays.Mods
protected readonly FillFlowContainer<ModSection> ModSectionsContainer; protected readonly FillFlowContainer<ModSection> ModSectionsContainer;
protected readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>(new Mod[] { }); protected readonly Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
protected readonly IBindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>(); protected readonly IBindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(OsuColour colours, IBindable<RulesetInfo> ruleset, AudioManager audio, Bindable<IEnumerable<Mod>> selectedMods) private void load(OsuColour colours, IBindable<RulesetInfo> ruleset, AudioManager audio, Bindable<IReadOnlyList<Mod>> mods)
{ {
LowMultiplierColour = colours.Red; LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green; HighMultiplierColour = colours.Green;
UnrankedLabel.Colour = colours.Blue; UnrankedLabel.Colour = colours.Blue;
Ruleset.BindTo(ruleset); Ruleset.BindTo(ruleset);
if (selectedMods != null) SelectedMods.BindTo(selectedMods); if (mods != null) SelectedMods.BindTo(mods);
sampleOn = audio.Sample.Get(@"UI/check-on"); sampleOn = audio.Sample.Get(@"UI/check-on");
sampleOff = audio.Sample.Get(@"UI/check-off"); sampleOff = audio.Sample.Get(@"UI/check-off");
@ -87,14 +87,14 @@ namespace osu.Game.Overlays.Mods
// attempt to re-select any already selected mods. // attempt to re-select any already selected mods.
// this may be the first time we are receiving the ruleset, in which case they will still match. // this may be the first time we are receiving the ruleset, in which case they will still match.
selectedModsChanged(new ValueChangedEvent<IEnumerable<Mod>>(SelectedMods.Value, SelectedMods.Value)); selectedModsChanged(new ValueChangedEvent<IReadOnlyList<Mod>>(SelectedMods.Value, SelectedMods.Value));
// write the mods back to the SelectedMods bindable in the case a change was not applicable. // write the mods back to the SelectedMods bindable in the case a change was not applicable.
// this generally isn't required as the previous line will perform deselection; just here for safety. // this generally isn't required as the previous line will perform deselection; just here for safety.
refreshSelectedMods(); refreshSelectedMods();
} }
private void selectedModsChanged(ValueChangedEvent<IEnumerable<Mod>> e) private void selectedModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> e)
{ {
foreach (ModSection section in ModSectionsContainer.Children) foreach (ModSection section in ModSectionsContainer.Children)
section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList()); section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList());

View File

@ -23,6 +23,7 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Music; using osu.Game.Overlays.Music;
using osu.Game.Rulesets.Mods;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -50,12 +51,15 @@ namespace osu.Game.Overlays
private BeatmapManager beatmaps; private BeatmapManager beatmaps;
private List<BeatmapSetInfo> beatmapSets; private List<BeatmapSetInfo> beatmapSets;
private BeatmapSetInfo currentSet;
private Container dragContainer; private Container dragContainer;
private Container playerContainer; private Container playerContainer;
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>(); [Resolved]
private Bindable<WorkingBeatmap> beatmap { get; set; }
[Resolved]
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
/// <summary> /// <summary>
/// Provide a source for the toolbar height. /// Provide a source for the toolbar height.
@ -74,7 +78,6 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(Bindable<WorkingBeatmap> beatmap, BeatmapManager beatmaps, OsuColour colours) private void load(Bindable<WorkingBeatmap> beatmap, BeatmapManager beatmaps, OsuColour colours)
{ {
this.beatmap.BindTo(beatmap);
this.beatmaps = beatmaps; this.beatmaps = beatmaps;
Children = new Drawable[] Children = new Drawable[]
@ -232,6 +235,7 @@ namespace osu.Game.Overlays
{ {
beatmap.BindValueChanged(beatmapChanged, true); beatmap.BindValueChanged(beatmapChanged, true);
beatmap.BindDisabledChanged(beatmapDisabledChanged, true); beatmap.BindDisabledChanged(beatmapDisabledChanged, true);
mods.BindValueChanged(_ => updateAudioAdjustments(), true);
base.LoadComplete(); base.LoadComplete();
} }
@ -355,10 +359,23 @@ namespace osu.Game.Overlays
progressBar.CurrentTime = 0; progressBar.CurrentTime = 0;
updateDisplay(current, direction); updateDisplay(current, direction);
updateAudioAdjustments();
queuedDirection = null; queuedDirection = null;
} }
private void updateAudioAdjustments()
{
var track = current?.Track;
if (track == null)
return;
track.ResetSpeedAdjustments();
foreach (var mod in mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(track);
}
private void currentTrackCompleted() => Schedule(() => private void currentTrackCompleted() => Schedule(() =>
{ {
if (!current.Track.Looping && !beatmap.Disabled && beatmapSets.Any()) if (!current.Track.Looping && !beatmap.Disabled && beatmapSets.Any())

View File

@ -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 osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Configuration;
@ -9,7 +10,7 @@ namespace osu.Game.Overlays.Settings
{ {
/// <summary> /// <summary>
/// A <see cref="SettingsSubsection"/> which provides subclasses with the <see cref="IRulesetConfigManager"/> /// A <see cref="SettingsSubsection"/> which provides subclasses with the <see cref="IRulesetConfigManager"/>
/// from the <see cref="Ruleset"/>'s <see cref="Ruleset.CreateConfig()"/>. /// from the <see cref="Ruleset"/>'s <see cref="Ruleset.CreateConfig(SettingsStore)"/>.
/// </summary> /// </summary>
public abstract class RulesetSettingsSubsection : SettingsSubsection public abstract class RulesetSettingsSubsection : SettingsSubsection
{ {

View File

@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Settings
{ {
protected override OsuDropdown<T> CreateDropdown() => new DropdownControl(); protected override OsuDropdown<T> CreateDropdown() => new DropdownControl();
protected class DropdownControl : OsuEnumDropdown<T> protected new class DropdownControl : OsuEnumDropdown<T>
{ {
public DropdownControl() public DropdownControl()
{ {

View File

@ -39,8 +39,7 @@ namespace osu.Game.Rulesets.Difficulty
{ {
mods = mods.Select(m => m.CreateCopy()).ToArray(); mods = mods.Select(m => m.CreateCopy()).ToArray();
beatmap.Mods.Value = mods; IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo);
var clock = new StopwatchClock(); var clock = new StopwatchClock();
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock)); mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
@ -106,7 +105,7 @@ namespace osu.Game.Rulesets.Difficulty
/// </summary> /// </summary>
public Mod[] CreateDifficultyAdjustmentModCombinations() public Mod[] CreateDifficultyAdjustmentModCombinations()
{ {
return createDifficultyAdjustmentModCombinations(Enumerable.Empty<Mod>(), DifficultyAdjustmentMods).ToArray(); return createDifficultyAdjustmentModCombinations(Array.Empty<Mod>(), DifficultyAdjustmentMods).ToArray();
IEnumerable<Mod> createDifficultyAdjustmentModCombinations(IEnumerable<Mod> currentSet, Mod[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) IEnumerable<Mod> createDifficultyAdjustmentModCombinations(IEnumerable<Mod> currentSet, Mod[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0)
{ {
@ -166,7 +165,7 @@ namespace osu.Game.Rulesets.Difficulty
/// <summary> /// <summary>
/// Creates the <see cref="Skill"/>s to calculate the difficulty of an <see cref="IBeatmap"/>. /// Creates the <see cref="Skill"/>s to calculate the difficulty of an <see cref="IBeatmap"/>.
/// </summary> /// </summary>
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty will be calculated.</param /// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty will be calculated.</param>
/// <returns>The <see cref="Skill"/>s.</returns> /// <returns>The <see cref="Skill"/>s.</returns>
protected abstract Skill[] CreateSkills(IBeatmap beatmap); protected abstract Skill[] CreateSkills(IBeatmap beatmap);
} }

View File

@ -26,8 +26,7 @@ namespace osu.Game.Rulesets.Difficulty
Ruleset = ruleset; Ruleset = ruleset;
Score = score; Score = score;
beatmap.Mods.Value = score.Mods; Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, score.Mods);
Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo);
Attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(score.Mods); Attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(score.Mods);

View File

@ -14,6 +14,7 @@ using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
@ -185,8 +186,8 @@ namespace osu.Game.Rulesets.Edit
} }
internal override DrawableEditRuleset CreateDrawableRuleset() internal override DrawableEditRuleset CreateDrawableRuleset()
=> new DrawableEditRuleset<TObject>(CreateDrawableRuleset(Ruleset, Beatmap.Value)); => new DrawableEditRuleset<TObject>(CreateDrawableRuleset(Ruleset, Beatmap.Value, Array.Empty<Mod>()));
protected abstract DrawableRuleset<TObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap); protected abstract DrawableRuleset<TObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods);
} }
} }

View File

@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose;
using osuTK; using osuTK;
@ -108,7 +109,8 @@ namespace osu.Game.Rulesets.Edit
} }
/// <summary> /// <summary>
/// Invokes <see cref="HitObject.ApplyDefaults"/>, refreshing <see cref="HitObject.NestedHitObjects"/> and parameters for the <see cref="HitObject"/>. /// Invokes <see cref="Objects.HitObject.ApplyDefaults(ControlPointInfo,BeatmapDifficulty)"/>,
/// refreshing <see cref="Objects.HitObject.NestedHitObjects"/> and parameters for the <see cref="HitObject"/>.
/// </summary> /// </summary>
protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.Value.Beatmap.ControlPointInfo, beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty); protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.Value.Beatmap.ControlPointInfo, beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty);

View File

@ -68,9 +68,11 @@ namespace osu.Game.Rulesets.Edit
get => state; get => state;
set set
{ {
if (state == value) return; if (state == value)
return;
state = value; state = value;
switch (state) switch (state)
{ {
case SelectionState.Selected: case SelectionState.Selected:
@ -82,6 +84,8 @@ namespace osu.Game.Rulesets.Edit
Deselected?.Invoke(this); Deselected?.Invoke(this);
break; break;
} }
StateChanged?.Invoke(state);
} }
} }

View File

@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Judgements
/// </summary> /// </summary>
public int MaxNumericResult => NumericResultFor(MaxResult); public int MaxNumericResult => NumericResultFor(MaxResult);
/// <summary>
/// The health increase for the maximum achievable result.
/// </summary>
public double MaxHealthIncrease => HealthIncreaseFor(MaxResult);
/// <summary> /// <summary>
/// Retrieves the numeric score representation of a <see cref="HitResult"/>. /// Retrieves the numeric score representation of a <see cref="HitResult"/>.
/// </summary> /// </summary>

View File

@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Judgements
/// </summary> /// </summary>
public int HighestComboAtJudgement { get; internal set; } public int HighestComboAtJudgement { get; internal set; }
/// <summary>
/// The health prior to this <see cref="JudgementResult"/> occurring.
/// </summary>
public double HealthAtJudgement { get; internal set; }
/// <summary> /// <summary>
/// Whether a miss or hit occurred. /// Whether a miss or hit occurred.
/// </summary> /// </summary>

View File

@ -2,14 +2,12 @@
// 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.Objects;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
/// <summary> /// <summary>
/// Interface for a <see cref="Mod"/> that applies changes to a <see cref="BeatmapConverter{TObject}"/>. /// Interface for a <see cref="Mod"/> that applies changes to a <see cref="BeatmapConverter{TObject}"/>.
/// </summary> /// </summary>
/// <typeparam name="TObject">The type of converted <see cref="HitObject"/>.</typeparam>
public interface IApplicableToBeatmapConverter : IApplicableMod public interface IApplicableToBeatmapConverter : IApplicableMod
{ {
/// <summary> /// <summary>

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods
public interface IApplicableToHitObject : IApplicableMod public interface IApplicableToHitObject : IApplicableMod
{ {
/// <summary> /// <summary>
/// Applies this <see cref="IApplicableToHitObject{TObject}"/> to a <see cref="HitObject"/>. /// Applies this <see cref="IApplicableToHitObject"/> to a <see cref="HitObject"/>.
/// </summary> /// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> to apply to.</param> /// <param name="hitObject">The <see cref="HitObject"/> to apply to.</param>
void ApplyToHitObject(HitObject hitObject); void ApplyToHitObject(HitObject hitObject);

View File

@ -7,6 +7,7 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Extensions.TypeExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Graphics; using osu.Game.Graphics;
@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
public bool AllJudged => Judged && NestedHitObjects.All(h => h.AllJudged); public bool AllJudged => Judged && NestedHitObjects.All(h => h.AllJudged);
/// <summary> /// <summary>
/// Whether this <see cref="DrawableHitObject"/> has been hit. This occurs if <see cref="Result.IsHit"/> is <see cref="true"/>. /// Whether this <see cref="DrawableHitObject"/> has been hit. This occurs if <see cref="Result"/> is hit.
/// Note: This does NOT include nested hitobjects. /// Note: This does NOT include nested hitobjects.
/// </summary> /// </summary>
public bool IsHit => Result?.IsHit ?? false; public bool IsHit => Result?.IsHit ?? false;
@ -223,7 +224,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
} }
/// <summary> /// <summary>
/// Will called at least once after the <see cref="LifetimeEnd"/> of this <see cref="DrawableHitObject"/> has been passed. /// Will called at least once after the <see cref="Drawable.LifetimeEnd"/> of this <see cref="DrawableHitObject"/> has been passed.
/// </summary> /// </summary>
internal void OnLifetimeEnd() internal void OnLifetimeEnd()
{ {

View File

@ -53,8 +53,6 @@ namespace osu.Game.Rulesets.Objects
[JsonIgnore] [JsonIgnore]
public bool Kiai { get; private set; } public bool Kiai { get; private set; }
private float overallDifficulty = BeatmapDifficulty.DEFAULT_DIFFICULTY;
/// <summary> /// <summary>
/// The hit windows for this <see cref="HitObject"/>. /// The hit windows for this <see cref="HitObject"/>.
/// </summary> /// </summary>
@ -115,7 +113,7 @@ namespace osu.Game.Rulesets.Objects
/// Creates the <see cref="HitWindows"/> for this <see cref="HitObject"/>. /// Creates the <see cref="HitWindows"/> for this <see cref="HitObject"/>.
/// This can be null to indicate that the <see cref="HitObject"/> has no <see cref="HitWindows"/>. /// This can be null to indicate that the <see cref="HitObject"/> has no <see cref="HitWindows"/>.
/// <para> /// <para>
/// This will only be invoked if <see cref="HitWindows"/> hasn't been set externally (e.g. from a <see cref="BeatmapConverter"/>. /// This will only be invoked if <see cref="HitWindows"/> hasn't been set externally (e.g. from a <see cref="BeatmapConverter{T}"/>.
/// </para> /// </para>
/// </summary> /// </summary>
protected virtual HitWindows CreateHitWindows() => new HitWindows(); protected virtual HitWindows CreateHitWindows() => new HitWindows();

View File

@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Objects
/// <summary> /// <summary>
/// Given a time offset, whether the <see cref="HitObject"/> can ever be hit in the future with a non-<see cref="HitResult.Miss"/> result. /// Given a time offset, whether the <see cref="HitObject"/> can ever be hit in the future with a non-<see cref="HitResult.Miss"/> result.
/// This happens if <paramref name="timeOffset"/> is less than what is required for a <see cref="SuccessfulHitWindow"/> result. /// This happens if <paramref name="timeOffset"/> is less than what is required for <see cref="LowestSuccessfulHitResult"/>.
/// </summary> /// </summary>
/// <param name="timeOffset">The time offset.</param> /// <param name="timeOffset">The time offset.</param>
/// <returns>Whether the <see cref="HitObject"/> can be hit at any point in the future from this time offset.</returns> /// <returns>Whether the <see cref="HitObject"/> can be hit at any point in the future from this time offset.</returns>

View File

@ -277,12 +277,5 @@ namespace osu.Game.Rulesets.Objects
return ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance.Equals(other.ExpectedDistance) && Type == other.Type; return ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance.Equals(other.ExpectedDistance) && Type == other.Type;
} }
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is SliderPath other && Equals(other);
}
} }
} }

View File

@ -53,9 +53,10 @@ namespace osu.Game.Rulesets
/// Attempt to create a hit renderer for a beatmap /// Attempt to create a hit renderer for a beatmap
/// </summary> /// </summary>
/// <param name="beatmap">The beatmap to create the hit renderer for.</param> /// <param name="beatmap">The beatmap to create the hit renderer for.</param>
/// <param name="mods">The <see cref="Mod"/>s to apply.</param>
/// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception> /// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception>
/// <returns></returns> /// <returns></returns>
public abstract DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap); public abstract DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods);
/// <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"/>.

View File

@ -154,7 +154,6 @@ namespace osu.Game.Rulesets.Scoring
/// <summary> /// <summary>
/// Notifies subscribers of <see cref="NewJudgement"/> that a new judgement has occurred. /// Notifies subscribers of <see cref="NewJudgement"/> that a new judgement has occurred.
/// </summary> /// </summary>
/// <param name="judgement">The judgement to notify subscribers of.</param>
/// <param name="result">The judgement scoring result to notify subscribers of.</param> /// <param name="result">The judgement scoring result to notify subscribers of.</param>
protected void NotifyNewJudgement(JudgementResult result) protected void NotifyNewJudgement(JudgementResult result)
{ {
@ -283,7 +282,6 @@ namespace osu.Game.Rulesets.Scoring
/// <summary> /// <summary>
/// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/>. /// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/>.
/// </summary> /// </summary>
/// <param name="judgement">The judgement to remove.</param>
/// <param name="result">The judgement scoring result.</param> /// <param name="result">The judgement scoring result.</param>
private void revertResult(JudgementResult result) private void revertResult(JudgementResult result)
{ {
@ -301,6 +299,7 @@ namespace osu.Game.Rulesets.Scoring
{ {
result.ComboAtJudgement = Combo.Value; result.ComboAtJudgement = Combo.Value;
result.HighestComboAtJudgement = HighestCombo.Value; result.HighestComboAtJudgement = HighestCombo.Value;
result.HealthAtJudgement = Health.Value;
JudgedHits++; JudgedHits++;
@ -332,17 +331,19 @@ namespace osu.Game.Rulesets.Scoring
baseScore += result.Judgement.NumericResultFor(result); baseScore += result.Judgement.NumericResultFor(result);
rollingMaxBaseScore += result.Judgement.MaxNumericResult; rollingMaxBaseScore += result.Judgement.MaxNumericResult;
} }
Health.Value += HealthAdjustmentFactorFor(result) * result.Judgement.HealthIncreaseFor(result);
} }
/// <summary> /// <summary>
/// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/>. /// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/>.
/// </summary> /// </summary>
/// <param name="judgement">The judgement to remove.</param>
/// <param name="result">The judgement scoring result.</param> /// <param name="result">The judgement scoring result.</param>
protected virtual void RevertResult(JudgementResult result) protected virtual void RevertResult(JudgementResult result)
{ {
Combo.Value = result.ComboAtJudgement; Combo.Value = result.ComboAtJudgement;
HighestCombo.Value = result.HighestComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement;
Health.Value = result.HealthAtJudgement;
JudgedHits--; JudgedHits--;
@ -358,6 +359,13 @@ namespace osu.Game.Rulesets.Scoring
} }
} }
/// <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;
private void updateScore() private void updateScore()
{ {
if (rollingMaxBaseScore != 0) if (rollingMaxBaseScore != 0)

View File

@ -11,7 +11,6 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
@ -82,7 +81,8 @@ namespace osu.Game.Rulesets.UI
/// <summary> /// <summary>
/// The mods which are to be applied. /// The mods which are to be applied.
/// </summary> /// </summary>
private readonly IEnumerable<Mod> mods; [Cached(typeof(IReadOnlyList<Mod>))]
private readonly IReadOnlyList<Mod> mods;
private FrameStabilityContainer frameStabilityContainer; private FrameStabilityContainer frameStabilityContainer;
@ -93,16 +93,19 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
/// <param name="ruleset">The ruleset being represented.</param> /// <param name="ruleset">The ruleset being represented.</param>
/// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param> /// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param>
protected DrawableRuleset(Ruleset ruleset, WorkingBeatmap workingBeatmap) /// <param name="mods">The <see cref="Mod"/>s to apply.</param>
protected DrawableRuleset(Ruleset ruleset, WorkingBeatmap workingBeatmap, IReadOnlyList<Mod> mods)
: base(ruleset) : base(ruleset)
{ {
Debug.Assert(workingBeatmap != null, "DrawableRuleset initialized with a null beatmap."); if (workingBeatmap == null)
throw new ArgumentException("Beatmap cannot be null.", nameof(workingBeatmap));
this.mods = mods.ToArray();
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
Beatmap = (Beatmap<TObject>)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo); Beatmap = (Beatmap<TObject>)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
mods = workingBeatmap.Mods.Value;
applyBeatmapMods(mods); applyBeatmapMods(mods);
KeyBindingInputManager = CreateInputManager(); KeyBindingInputManager = CreateInputManager();
@ -223,6 +226,12 @@ namespace osu.Game.Rulesets.UI
if (replayInputManager.ReplayInputHandler != null) if (replayInputManager.ReplayInputHandler != null)
replayInputManager.ReplayInputHandler.GamefieldToScreenSpace = Playfield.GamefieldToScreenSpace; replayInputManager.ReplayInputHandler.GamefieldToScreenSpace = Playfield.GamefieldToScreenSpace;
if (!ProvidingUserCursor)
{
// The cursor is hidden by default (see Playfield.load()), but should be shown when there's a replay
Playfield.Cursor?.Show();
}
} }
/// <summary> /// <summary>
@ -255,7 +264,7 @@ namespace osu.Game.Rulesets.UI
/// Applies the active mods to the Beatmap. /// Applies the active mods to the Beatmap.
/// </summary> /// </summary>
/// <param name="mods"></param> /// <param name="mods"></param>
private void applyBeatmapMods(IEnumerable<Mod> mods) private void applyBeatmapMods(IReadOnlyList<Mod> mods)
{ {
if (mods == null) if (mods == null)
return; return;
@ -267,8 +276,9 @@ namespace osu.Game.Rulesets.UI
/// <summary> /// <summary>
/// Applies the active mods to this DrawableRuleset. /// Applies the active mods to this DrawableRuleset.
/// </summary> /// </summary>
/// <param name="mods"></param> /// <param name="mods">The <see cref="Mod"/>s to apply.</param>
private void applyRulesetMods(IEnumerable<Mod> mods, OsuConfigManager config) /// <param name="config">The <see cref="OsuConfigManager"/> to apply.</param>
private void applyRulesetMods(IReadOnlyList<Mod> mods, OsuConfigManager config)
{ {
if (mods == null) if (mods == null)
return; return;

View File

@ -68,6 +68,8 @@ namespace osu.Game.Rulesets.UI
private const double sixty_frame_time = 1000.0 / 60; private const double sixty_frame_time = 1000.0 / 60;
private bool firstConsumption = true;
public override bool UpdateSubTree() public override bool UpdateSubTree()
{ {
requireMoreUpdateLoops = true; requireMoreUpdateLoops = true;
@ -103,9 +105,20 @@ namespace osu.Game.Rulesets.UI
try try
{ {
if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f) if (firstConsumption)
{ {
newProposedTime = manualClock.Rate > 0 // On the first update, frame-stability seeking would result in unexpected/unwanted behaviour.
// Instead we perform an initial seek to the proposed time.
manualClock.CurrentTime = newProposedTime;
// do a second process to clear out ElapsedTime
framedClock.ProcessFrame();
firstConsumption = false;
}
else if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
{
newProposedTime = newProposedTime > manualClock.CurrentTime
? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time) ? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time)
: Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time); : Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time);
} }

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.UI
public class GameplayCursorContainer : CursorContainer public class GameplayCursorContainer : CursorContainer
{ {
/// <summary> /// <summary>
/// Because Show/Hide are executed by a parent, <see cref="State"/> is updated immediately even if the cursor /// Because Show/Hide are executed by a parent, <see cref="VisibilityContainer.State"/> is updated immediately even if the cursor
/// is in a non-updating state (via <see cref="FrameStabilityContainer"/> limitations). /// is in a non-updating state (via <see cref="FrameStabilityContainer"/> limitations).
/// ///
/// This holds the true visibility value. /// This holds the true visibility value.

View File

@ -57,17 +57,24 @@ namespace osu.Game.Rulesets.UI
hitObjectContainerLazy = new Lazy<HitObjectContainer>(CreateHitObjectContainer); hitObjectContainerLazy = new Lazy<HitObjectContainer>(CreateHitObjectContainer);
} }
private WorkingBeatmap beatmap; [Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; }
[Resolved]
private IReadOnlyList<Mod> mods { get; set; }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(IBindable<WorkingBeatmap> beatmap) private void load()
{ {
this.beatmap = beatmap.Value;
Cursor = CreateCursor(); Cursor = CreateCursor();
if (Cursor != null) if (Cursor != null)
{
// initial showing of the cursor will be handed by MenuCursorContainer (via DrawableRuleset's IProvideCursor implementation).
Cursor.Hide();
AddInternal(Cursor); AddInternal(Cursor);
} }
}
/// <summary> /// <summary>
/// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield. /// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield.
@ -93,7 +100,6 @@ namespace osu.Game.Rulesets.UI
/// <summary> /// <summary>
/// Provide an optional cursor which is to be used for gameplay. /// Provide an optional cursor which is to be used for gameplay.
/// If providing a cursor, <see cref="CursorTargetContainer"/> must also point to a valid target container.
/// </summary> /// </summary>
/// <returns>The cursor, or null if a cursor is not rqeuired.</returns> /// <returns>The cursor, or null if a cursor is not rqeuired.</returns>
protected virtual GameplayCursorContainer CreateCursor() => null; protected virtual GameplayCursorContainer CreateCursor() => null;
@ -123,7 +129,7 @@ namespace osu.Game.Rulesets.UI
base.Update(); base.Update();
if (beatmap != null) if (beatmap != null)
foreach (var mod in beatmap.Mods.Value) foreach (var mod in mods)
if (mod is IUpdatableByPlayfield updatable) if (mod is IUpdatableByPlayfield updatable)
updatable.Update(this); updatable.Update(this);
} }

View File

@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
/// <param name="currentTime">The current time.</param> /// <param name="currentTime">The current time.</param>
/// <param name="timeRange">The amount of visible time.</param> /// <param name="timeRange">The amount of visible time.</param>
/// <param name="scrollLength">The absolute spatial length through <see cref="timeRange"/>.</param> /// <param name="scrollLength">The absolute spatial length through <see cref="timeRange"/>.</param>
/// <returns>The time at which <see cref="PositionAt(t)"/> == <paramref name="position"/>.</returns> /// <returns>The time at which <see cref="PositionAt(double,double,double,float)"/> == <paramref name="position"/>.</returns>
double TimeAt(float position, double currentTime, double timeRange, float scrollLength); double TimeAt(float position, double currentTime, double timeRange, float scrollLength);
/// <summary> /// <summary>

View File

@ -13,6 +13,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Timing; using osu.Game.Rulesets.Timing;
@ -64,7 +65,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
protected virtual ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Sequential; protected virtual ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Sequential;
/// <summary> /// <summary>
/// Whether the player can change <see cref="VisibleTimeRange"/>. /// Whether the player can change <see cref="TimeRange"/>.
/// </summary> /// </summary>
protected virtual bool UserScrollSpeedAdjustment => true; protected virtual bool UserScrollSpeedAdjustment => true;
@ -80,8 +81,8 @@ namespace osu.Game.Rulesets.UI.Scrolling
[Cached(Type = typeof(IScrollingInfo))] [Cached(Type = typeof(IScrollingInfo))]
private readonly LocalScrollingInfo scrollingInfo; private readonly LocalScrollingInfo scrollingInfo;
protected DrawableScrollingRuleset(Ruleset ruleset, WorkingBeatmap beatmap) protected DrawableScrollingRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap) : base(ruleset, beatmap, mods)
{ {
scrollingInfo = new LocalScrollingInfo(); scrollingInfo = new LocalScrollingInfo();
scrollingInfo.Direction.BindTo(Direction); scrollingInfo.Direction.BindTo(Direction);

View File

@ -21,7 +21,7 @@ namespace osu.Game.Screens
//public float ParallaxAmount { set => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * value; } //public float ParallaxAmount { set => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * value; }
public new void Push(BackgroundScreen screen) public void Push(BackgroundScreen screen)
{ {
if (screen == null) if (screen == null)
return; return;

View File

@ -121,6 +121,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// Handle a blueprint requesting selection. /// Handle a blueprint requesting selection.
/// </summary> /// </summary>
/// <param name="blueprint">The blueprint.</param> /// <param name="blueprint">The blueprint.</param>
/// <param name="state">The input state at the point of selection.</param>
internal void HandleSelectionRequested(SelectionBlueprint blueprint, InputState state) internal void HandleSelectionRequested(SelectionBlueprint blueprint, InputState state)
{ {
if (state.Keyboard.ControlPressed) if (state.Keyboard.ControlPressed)
@ -166,8 +167,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
var topLeft = new Vector2(float.MaxValue, float.MaxValue); var topLeft = new Vector2(float.MaxValue, float.MaxValue);
var bottomRight = new Vector2(float.MinValue, float.MinValue); var bottomRight = new Vector2(float.MinValue, float.MinValue);
bool hasSelection = false;
foreach (var blueprint in selectedBlueprints) foreach (var blueprint in selectedBlueprints)
{ {
topLeft = Vector2.ComponentMin(topLeft, ToLocalSpace(blueprint.SelectionQuad.TopLeft)); topLeft = Vector2.ComponentMin(topLeft, ToLocalSpace(blueprint.SelectionQuad.TopLeft));

View File

@ -92,13 +92,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
} }
} }
protected override void Update()
{
base.Update();
zoomedContent.Width = DrawWidth * currentZoom;
}
protected override bool OnScroll(ScrollEvent e) protected override bool OnScroll(ScrollEvent e)
{ {
if (e.IsPrecise) if (e.IsPrecise)
@ -138,7 +131,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private readonly float scrollOffset; private readonly float scrollOffset;
/// <summary> /// <summary>
/// Transforms <see cref="TimeTimelinem"/> to a new value. /// Transforms <see cref="ZoomableScrollContainer.currentZoom"/> to a new value.
/// </summary> /// </summary>
/// <param name="focusPoint">The focus point in absolute coordinates local to the content.</param> /// <param name="focusPoint">The focus point in absolute coordinates local to the content.</param>
/// <param name="contentSize">The size of the content.</param> /// <param name="contentSize">The size of the content.</param>
@ -169,6 +162,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
float targetOffset = expectedWidth * (focusPoint / contentSize) - focusOffset; float targetOffset = expectedWidth * (focusPoint / contentSize) - focusOffset;
d.currentZoom = newZoom; d.currentZoom = newZoom;
d.zoomedContent.Width = d.DrawWidth * d.currentZoom;
// Temporarily here to make sure ScrollTo gets the correct DrawSize for scrollable area.
// TODO: Make sure draw size gets invalidated properly on the framework side, and remove this once it is.
d.Invalidate(Invalidation.DrawSize);
d.ScrollTo(targetOffset, false); d.ScrollTo(targetOffset, false);
} }

View File

@ -65,9 +65,6 @@ namespace osu.Game.Screens.Edit
dependencies.Cache(beatDivisor); dependencies.Cache(beatDivisor);
EditorMenuBar menuBar; EditorMenuBar menuBar;
TimeInfoContainer timeInfo;
SummaryTimeline timeline;
PlaybackControl playback;
var fileMenuItems = new List<MenuItem>(); var fileMenuItems = new List<MenuItem>();
if (RuntimeInfo.IsDesktop) if (RuntimeInfo.IsDesktop)

Some files were not shown because too many files have changed in this diff Show More