mirror of
https://github.com/ppy/osu.git
synced 2025-02-15 23:42:55 +08:00
Merge branch 'master' into fallback-intro
This commit is contained in:
commit
574e8444b0
@ -2,12 +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 NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -18,7 +18,6 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
[HeadlessTest]
|
[HeadlessTest]
|
||||||
public class TestSceneDrainingHealthProcessor : OsuTestScene
|
public class TestSceneDrainingHealthProcessor : OsuTestScene
|
||||||
{
|
{
|
||||||
private Bindable<bool> breakTime;
|
|
||||||
private HealthProcessor processor;
|
private HealthProcessor processor;
|
||||||
private ManualClock clock;
|
private ManualClock clock;
|
||||||
|
|
||||||
@ -41,6 +40,64 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
assertHealthEqualTo(1);
|
assertHealthEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHealthDrainBetweenBreakAndObjects()
|
||||||
|
{
|
||||||
|
createProcessor(createBeatmap(0, 2000, new BreakPeriod(325, 375)));
|
||||||
|
|
||||||
|
// 275 300 325 350 375 400 425
|
||||||
|
// hitobjects o o
|
||||||
|
// break [-------------]
|
||||||
|
// no drain [---------------------------]
|
||||||
|
|
||||||
|
setTime(285);
|
||||||
|
setHealth(1);
|
||||||
|
|
||||||
|
setTime(295);
|
||||||
|
assertHealthNotEqualTo(1);
|
||||||
|
|
||||||
|
setTime(305);
|
||||||
|
setHealth(1);
|
||||||
|
|
||||||
|
setTime(315);
|
||||||
|
assertHealthEqualTo(1);
|
||||||
|
|
||||||
|
setTime(365);
|
||||||
|
assertHealthEqualTo(1);
|
||||||
|
|
||||||
|
setTime(395);
|
||||||
|
assertHealthEqualTo(1);
|
||||||
|
|
||||||
|
setTime(425);
|
||||||
|
assertHealthNotEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHealthDrainDuringMaximalBreak()
|
||||||
|
{
|
||||||
|
createProcessor(createBeatmap(0, 2000, new BreakPeriod(300, 400)));
|
||||||
|
|
||||||
|
// 275 300 325 350 375 400 425
|
||||||
|
// hitobjects o o
|
||||||
|
// break [---------------------------]
|
||||||
|
// no drain [---------------------------]
|
||||||
|
|
||||||
|
setTime(285);
|
||||||
|
setHealth(1);
|
||||||
|
|
||||||
|
setTime(295);
|
||||||
|
assertHealthNotEqualTo(1);
|
||||||
|
|
||||||
|
setTime(305);
|
||||||
|
setHealth(1);
|
||||||
|
|
||||||
|
setTime(395);
|
||||||
|
assertHealthEqualTo(1);
|
||||||
|
|
||||||
|
setTime(425);
|
||||||
|
assertHealthNotEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestHealthNotDrainedAfterGameplayEnd()
|
public void TestHealthNotDrainedAfterGameplayEnd()
|
||||||
{
|
{
|
||||||
@ -54,18 +111,6 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
assertHealthEqualTo(1);
|
assertHealthEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestHealthNotDrainedDuringBreak()
|
|
||||||
{
|
|
||||||
createProcessor(createBeatmap(0, 2000));
|
|
||||||
setBreak(true);
|
|
||||||
|
|
||||||
setTime(700);
|
|
||||||
assertHealthEqualTo(1);
|
|
||||||
setTime(900);
|
|
||||||
assertHealthEqualTo(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestHealthDrainedDuringGameplay()
|
public void TestHealthDrainedDuringGameplay()
|
||||||
{
|
{
|
||||||
@ -112,30 +157,31 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
assertHealthNotEqualTo(1);
|
assertHealthNotEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Beatmap createBeatmap(double startTime, double endTime)
|
private Beatmap createBeatmap(double startTime, double endTime, params BreakPeriod[] breaks)
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap
|
var beatmap = new Beatmap
|
||||||
{
|
{
|
||||||
BeatmapInfo = { BaseDifficulty = { DrainRate = 5 } },
|
BeatmapInfo = { BaseDifficulty = { DrainRate = 10 } },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (double time = startTime; time <= endTime; time += 100)
|
for (double time = startTime; time <= endTime; time += 100)
|
||||||
|
{
|
||||||
beatmap.HitObjects.Add(new JudgeableHitObject { StartTime = time });
|
beatmap.HitObjects.Add(new JudgeableHitObject { StartTime = time });
|
||||||
|
}
|
||||||
|
|
||||||
|
beatmap.Breaks.AddRange(breaks);
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createProcessor(Beatmap beatmap) => AddStep("create processor", () =>
|
private void createProcessor(Beatmap beatmap) => AddStep("create processor", () =>
|
||||||
{
|
{
|
||||||
breakTime = new Bindable<bool>();
|
|
||||||
|
|
||||||
Child = processor = new DrainingHealthProcessor(beatmap.HitObjects[0].StartTime).With(d =>
|
Child = processor = new DrainingHealthProcessor(beatmap.HitObjects[0].StartTime).With(d =>
|
||||||
{
|
{
|
||||||
d.RelativeSizeAxes = Axes.Both;
|
d.RelativeSizeAxes = Axes.Both;
|
||||||
d.Clock = new FramedClock(clock = new ManualClock());
|
d.Clock = new FramedClock(clock = new ManualClock());
|
||||||
});
|
});
|
||||||
|
|
||||||
processor.IsBreakTime.BindTo(breakTime);
|
|
||||||
processor.ApplyBeatmap(beatmap);
|
processor.ApplyBeatmap(beatmap);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -143,8 +189,6 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
|
|
||||||
private void setHealth(double health) => AddStep($"set health = {health}", () => processor.Health.Value = health);
|
private void setHealth(double health) => AddStep($"set health = {health}", () => processor.Health.Value = health);
|
||||||
|
|
||||||
private void setBreak(bool enabled) => AddStep($"{(enabled ? "enable" : "disable")} break", () => breakTime.Value = enabled);
|
|
||||||
|
|
||||||
private void assertHealthEqualTo(double value)
|
private void assertHealthEqualTo(double value)
|
||||||
=> AddAssert($"health = {value}", () => Precision.AlmostEquals(value, processor.Health.Value, 0.0001f));
|
=> AddAssert($"health = {value}", () => Precision.AlmostEquals(value, processor.Health.Value, 0.0001f));
|
||||||
|
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Scoring
|
namespace osu.Game.Rulesets.Scoring
|
||||||
{
|
{
|
||||||
@ -47,6 +49,8 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
private double targetMinimumHealth;
|
private double targetMinimumHealth;
|
||||||
private double drainRate = 1;
|
private double drainRate = 1;
|
||||||
|
|
||||||
|
private PeriodTracker noDrainPeriodTracker;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="DrainingHealthProcessor"/>.
|
/// Creates a new <see cref="DrainingHealthProcessor"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -60,14 +64,14 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if (!IsBreakTime.Value)
|
if (noDrainPeriodTracker?.IsInAny(Time.Current) == true)
|
||||||
{
|
return;
|
||||||
// When jumping in and out of gameplay time within a single frame, health should only be drained for the period within the gameplay time
|
|
||||||
double lastGameplayTime = Math.Clamp(Time.Current - Time.Elapsed, drainStartTime, gameplayEndTime);
|
|
||||||
double currentGameplayTime = Math.Clamp(Time.Current, drainStartTime, gameplayEndTime);
|
|
||||||
|
|
||||||
Health.Value -= drainRate * (currentGameplayTime - lastGameplayTime);
|
// When jumping in and out of gameplay time within a single frame, health should only be drained for the period within the gameplay time
|
||||||
}
|
double lastGameplayTime = Math.Clamp(Time.Current - Time.Elapsed, drainStartTime, gameplayEndTime);
|
||||||
|
double currentGameplayTime = Math.Clamp(Time.Current, drainStartTime, gameplayEndTime);
|
||||||
|
|
||||||
|
Health.Value -= drainRate * (currentGameplayTime - lastGameplayTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ApplyBeatmap(IBeatmap beatmap)
|
public override void ApplyBeatmap(IBeatmap beatmap)
|
||||||
@ -77,6 +81,19 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
if (beatmap.HitObjects.Count > 0)
|
if (beatmap.HitObjects.Count > 0)
|
||||||
gameplayEndTime = beatmap.HitObjects[^1].GetEndTime();
|
gameplayEndTime = beatmap.HitObjects[^1].GetEndTime();
|
||||||
|
|
||||||
|
noDrainPeriodTracker = new PeriodTracker(beatmap.Breaks.Select(breakPeriod => new Period(
|
||||||
|
beatmap.HitObjects
|
||||||
|
.Select(hitObject => hitObject.GetEndTime())
|
||||||
|
.Where(endTime => endTime <= breakPeriod.StartTime)
|
||||||
|
.DefaultIfEmpty(double.MinValue)
|
||||||
|
.Last(),
|
||||||
|
beatmap.HitObjects
|
||||||
|
.Select(hitObject => hitObject.StartTime)
|
||||||
|
.Where(startTime => startTime >= breakPeriod.EndTime)
|
||||||
|
.DefaultIfEmpty(double.MaxValue)
|
||||||
|
.First()
|
||||||
|
)));
|
||||||
|
|
||||||
targetMinimumHealth = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target);
|
targetMinimumHealth = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target);
|
||||||
|
|
||||||
base.ApplyBeatmap(beatmap);
|
base.ApplyBeatmap(beatmap);
|
||||||
|
@ -26,11 +26,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly BindableDouble Health = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
public readonly BindableDouble Health = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether gameplay is currently in a break.
|
|
||||||
/// </summary>
|
|
||||||
public readonly IBindable<bool> IsBreakTime = new Bindable<bool>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this ScoreProcessor has already triggered the failed state.
|
/// Whether this ScoreProcessor has already triggered the failed state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -259,8 +259,6 @@ namespace osu.Game.Screens.Play
|
|||||||
Breaks = working.Beatmap.Breaks
|
Breaks = working.Beatmap.Breaks
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
HealthProcessor.IsBreakTime.BindTo(breakTracker.IsBreakTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addOverlayComponents(Container target, WorkingBeatmap working)
|
private void addOverlayComponents(Container target, WorkingBeatmap working)
|
||||||
|
Loading…
Reference in New Issue
Block a user