mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 08:55:35 +08:00
Merge remote-tracking branch 'origin/master' into new-diffcalc-taiko
# Conflicts: # osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs
This commit is contained in:
commit
71ef039606
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||
{
|
||||
if (!beatmap.HitObjects.Any())
|
||||
return new CatchDifficultyAttributes(mods, 0);
|
||||
@ -59,12 +59,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
||||
|
||||
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
||||
if (!calculateStrainValues(difficultyHitObjects, clockRate))
|
||||
return new CatchDifficultyAttributes(mods, 0);
|
||||
|
||||
// this is the same as osu!, so there's potential to share the implementation... maybe
|
||||
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
|
||||
double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor;
|
||||
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, clockRate)) * star_scaling_factor;
|
||||
|
||||
return new CatchDifficultyAttributes(mods, starRating)
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||
{
|
||||
if (!beatmap.HitObjects.Any())
|
||||
return new ManiaDifficultyAttributes(mods, 0);
|
||||
@ -50,15 +50,15 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
// Note: Stable sort is done so that the ordering of hitobjects with equal start times doesn't change
|
||||
difficultyHitObjects.AddRange(beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime));
|
||||
|
||||
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
||||
if (!calculateStrainValues(difficultyHitObjects, clockRate))
|
||||
return new ManiaDifficultyAttributes(mods, 0);
|
||||
|
||||
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
|
||||
double starRating = calculateDifficulty(difficultyHitObjects, clockRate) * star_scaling_factor;
|
||||
|
||||
return new ManiaDifficultyAttributes(mods, starRating)
|
||||
{
|
||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
|
||||
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate
|
||||
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -23,19 +23,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
{
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||
{
|
||||
if (!beatmap.HitObjects.Any())
|
||||
return new OsuDifficultyAttributes(mods, 0);
|
||||
|
||||
OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast<OsuHitObject>().ToList(), timeRate);
|
||||
OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast<OsuHitObject>().ToList(), clockRate);
|
||||
Skill[] skills =
|
||||
{
|
||||
new Aim(),
|
||||
new Speed()
|
||||
};
|
||||
|
||||
double sectionLength = section_length * timeRate;
|
||||
double sectionLength = section_length * clockRate;
|
||||
|
||||
// The first object doesn't generate a strain, so we begin with an incremented section end
|
||||
double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength;
|
||||
@ -66,8 +66,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
|
||||
|
||||
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||
double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
|
||||
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
|
||||
double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate;
|
||||
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
|
||||
int maxCombo = beatmap.HitObjects.Count;
|
||||
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
|
||||
|
74
osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
Normal file
74
osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
Normal file
@ -0,0 +1,74 @@
|
||||
// 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.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
internal class OsuModGrow : Mod, IApplicableToDrawableHitObjects
|
||||
{
|
||||
public override string Name => "Grow";
|
||||
|
||||
public override string Acronym => "GR";
|
||||
|
||||
public override FontAwesome Icon => FontAwesome.fa_arrows_v;
|
||||
|
||||
public override ModType Type => ModType.Fun;
|
||||
|
||||
public override string Description => "Hit them at the right size!";
|
||||
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
{
|
||||
foreach (var drawable in drawables)
|
||||
{
|
||||
switch (drawable)
|
||||
{
|
||||
case DrawableSpinner _:
|
||||
continue;
|
||||
default:
|
||||
drawable.ApplyCustomUpdateState += ApplyCustomState;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state)
|
||||
{
|
||||
var h = (OsuHitObject)drawable.HitObject;
|
||||
|
||||
// apply grow effect
|
||||
switch (drawable)
|
||||
{
|
||||
case DrawableSliderHead _:
|
||||
case DrawableSliderTail _:
|
||||
// special cases we should *not* be scaling.
|
||||
break;
|
||||
case DrawableSlider _:
|
||||
case DrawableHitCircle _:
|
||||
{
|
||||
using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
|
||||
drawable.ScaleTo(0.5f).Then().ScaleTo(1, h.TimePreempt, Easing.OutSine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// remove approach circles
|
||||
switch (drawable)
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
// we don't want to see the approach circle
|
||||
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
|
||||
circle.ApproachCircle.Hide();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using osuTK;
|
||||
@ -27,40 +28,58 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
|
||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||
|
||||
private readonly Container explodeContainer;
|
||||
|
||||
private readonly Container scaleContainer;
|
||||
|
||||
public DrawableHitCircle(HitCircle h)
|
||||
: base(h)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Position = HitObject.StackedPosition;
|
||||
Scale = new Vector2(h.Scale);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
glow = new GlowPiece(),
|
||||
circle = new CirclePiece
|
||||
scaleContainer = new Container
|
||||
{
|
||||
Hit = () =>
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Child = explodeContainer = new Container
|
||||
{
|
||||
if (AllJudged)
|
||||
return false;
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
glow = new GlowPiece(),
|
||||
circle = new CirclePiece
|
||||
{
|
||||
Hit = () =>
|
||||
{
|
||||
if (AllJudged)
|
||||
return false;
|
||||
|
||||
UpdateResult(true);
|
||||
return true;
|
||||
},
|
||||
UpdateResult(true);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
number = new NumberPiece
|
||||
{
|
||||
Text = (HitObject.IndexInCurrentCombo + 1).ToString(),
|
||||
},
|
||||
ring = new RingPiece(),
|
||||
flash = new FlashPiece(),
|
||||
explode = new ExplodePiece(),
|
||||
ApproachCircle = new ApproachCircle
|
||||
{
|
||||
Alpha = 0,
|
||||
Scale = new Vector2(4),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
number = new NumberPiece
|
||||
{
|
||||
Text = (HitObject.IndexInCurrentCombo + 1).ToString(),
|
||||
},
|
||||
ring = new RingPiece(),
|
||||
flash = new FlashPiece(),
|
||||
explode = new ExplodePiece(),
|
||||
ApproachCircle = new ApproachCircle
|
||||
{
|
||||
Alpha = 0,
|
||||
Scale = new Vector2(4),
|
||||
}
|
||||
};
|
||||
|
||||
//may not be so correct
|
||||
@ -72,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
||||
stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
||||
scaleBindable.BindValueChanged(v => Scale = new Vector2(v));
|
||||
scaleBindable.BindValueChanged(v => scaleContainer.Scale = new Vector2(v), true);
|
||||
|
||||
positionBindable.BindTo(HitObject.PositionBindable);
|
||||
stackHeightBindable.BindTo(HitObject.StackHeightBindable);
|
||||
@ -156,8 +175,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
circle.FadeOut();
|
||||
number.FadeOut();
|
||||
|
||||
this.FadeOut(800)
|
||||
.ScaleTo(Scale * 1.5f, 400, Easing.OutQuad);
|
||||
this.FadeOut(800);
|
||||
explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad);
|
||||
}
|
||||
|
||||
Expire();
|
||||
|
@ -124,6 +124,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
return new Mod[] {
|
||||
new OsuModTransform(),
|
||||
new OsuModWiggle(),
|
||||
new OsuModGrow()
|
||||
};
|
||||
default:
|
||||
return new Mod[] { };
|
||||
|
@ -146,7 +146,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods { get; }
|
||||
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) => throw new NotImplementedException();
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
115
osu.Game.Tests/NonVisual/LimitedCapacityStackTest.cs
Normal file
115
osu.Game.Tests/NonVisual/LimitedCapacityStackTest.cs
Normal file
@ -0,0 +1,115 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[TestFixture]
|
||||
public class LimitedCapacityStackTest
|
||||
{
|
||||
private const int capacity = 3;
|
||||
|
||||
private LimitedCapacityStack<int> stack;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
stack = new LimitedCapacityStack<int>(capacity);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyStack()
|
||||
{
|
||||
Assert.AreEqual(0, stack.Count);
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
int unused = stack[0];
|
||||
});
|
||||
|
||||
int count = 0;
|
||||
foreach (var unused in stack)
|
||||
count++;
|
||||
|
||||
Assert.AreEqual(0, count);
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public void TestInRangeElements(int count)
|
||||
{
|
||||
// e.g. 0 -> 1 -> 2
|
||||
for (int i = 0; i < count; i++)
|
||||
stack.Push(i);
|
||||
|
||||
Assert.AreEqual(count, stack.Count);
|
||||
|
||||
// e.g. 2 -> 1 -> 0 (reverse order)
|
||||
for (int i = 0; i < stack.Count; i++)
|
||||
Assert.AreEqual(count - 1 - i, stack[i]);
|
||||
|
||||
// e.g. indices 3, 4, 5, 6 (out of range)
|
||||
for (int i = stack.Count; i < stack.Count + capacity; i++)
|
||||
{
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
int unused = stack[i];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(4)]
|
||||
[TestCase(5)]
|
||||
[TestCase(6)]
|
||||
public void TestOverflowElements(int count)
|
||||
{
|
||||
// e.g. 0 -> 1 -> 2 -> 3
|
||||
for (int i = 0; i < count; i++)
|
||||
stack.Push(i);
|
||||
|
||||
Assert.AreEqual(capacity, stack.Count);
|
||||
|
||||
// e.g. 3 -> 2 -> 1 (reverse order)
|
||||
for (int i = 0; i < stack.Count; i++)
|
||||
Assert.AreEqual(count - 1 - i, stack[i]);
|
||||
|
||||
// e.g. indices 3, 4, 5, 6 (out of range)
|
||||
for (int i = stack.Count; i < stack.Count + capacity; i++)
|
||||
{
|
||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
||||
{
|
||||
int unused = stack[i];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
[TestCase(4)]
|
||||
[TestCase(5)]
|
||||
[TestCase(6)]
|
||||
public void TestEnumerator(int count)
|
||||
{
|
||||
// e.g. 0 -> 1 -> 2 -> 3
|
||||
for (int i = 0; i < count; i++)
|
||||
stack.Push(i);
|
||||
|
||||
int enumeratorCount = 0;
|
||||
int expectedValue = count - 1;
|
||||
|
||||
foreach (var item in stack)
|
||||
{
|
||||
Assert.AreEqual(expectedValue, item);
|
||||
enumeratorCount++;
|
||||
expectedValue--;
|
||||
}
|
||||
|
||||
Assert.AreEqual(stack.Count, enumeratorCount);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,13 +7,12 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
public class DifficultyAttributes
|
||||
{
|
||||
public readonly Mod[] Mods;
|
||||
public Mod[] Mods;
|
||||
|
||||
public double StarRating;
|
||||
|
||||
public DifficultyAttributes(Mod[] mods)
|
||||
public DifficultyAttributes()
|
||||
{
|
||||
Mods = mods;
|
||||
}
|
||||
|
||||
public DifficultyAttributes(Mod[] mods, double starRating)
|
||||
|
@ -23,17 +23,18 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||
{
|
||||
var attributes = CreateDifficultyAttributes(mods);
|
||||
var attributes = CreateDifficultyAttributes();
|
||||
attributes.Mods = mods;
|
||||
|
||||
if (!beatmap.HitObjects.Any())
|
||||
return attributes;
|
||||
|
||||
var difficultyHitObjects = CreateDifficultyHitObjects(beatmap, timeRate).OrderBy(h => h.BaseObject.StartTime).ToList();
|
||||
var difficultyHitObjects = CreateDifficultyHitObjects(beatmap, clockRate).OrderBy(h => h.BaseObject.StartTime).ToList();
|
||||
var skills = CreateSkills();
|
||||
|
||||
double sectionLength = SectionLength * timeRate;
|
||||
double sectionLength = SectionLength * clockRate;
|
||||
|
||||
// The first object doesn't generate a strain, so we begin with an incremented section end
|
||||
double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength;
|
||||
@ -59,7 +60,7 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
foreach (Skill s in skills)
|
||||
s.SaveCurrentPeak();
|
||||
|
||||
PopulateAttributes(attributes, beatmap, skills, timeRate);
|
||||
PopulateAttributes(attributes, beatmap, skills, clockRate);
|
||||
|
||||
return attributes;
|
||||
}
|
||||
@ -112,16 +113,16 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
/// <param name="attributes">The <see cref="DifficultyAttributes"/> to populate with information about the difficulty of <paramref name="beatmap"/>.</param>
|
||||
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty was processed.</param>
|
||||
/// <param name="skills">The skills which processed the difficulty.</param>
|
||||
/// <param name="timeRate">The rate of time in <paramref name="beatmap"/>.</param>
|
||||
protected abstract void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate);
|
||||
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
|
||||
protected abstract void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double clockRate);
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates <see cref="DifficultyHitObject"/>s to be processed from <see cref="HitObject"/>s in the <see cref="IBeatmap"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The <see cref="IBeatmap"/> providing the <see cref="HitObject"/>s to enumerate.</param>
|
||||
/// <param name="timeRate">The rate of time in <paramref name="beatmap"/>.</param>
|
||||
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
|
||||
/// <returns>The enumerated <see cref="DifficultyHitObject"/>s.</returns>
|
||||
protected abstract IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate);
|
||||
protected abstract IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="Skill"/>s to calculate the difficulty of <see cref="DifficultyHitObject"/>s.
|
||||
@ -132,8 +133,7 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
/// <summary>
|
||||
/// Creates an empty <see cref="DifficultyAttributes"/>.
|
||||
/// </summary>
|
||||
/// <param name="mods">The <see cref="Mod"/>s which difficulty is being processed with.</param>
|
||||
/// <returns>The empty <see cref="DifficultyAttributes"/>.</returns>
|
||||
protected abstract DifficultyAttributes CreateDifficultyAttributes(Mod[] mods);
|
||||
protected abstract DifficultyAttributes CreateDifficultyAttributes();
|
||||
}
|
||||
}
|
||||
|
@ -100,8 +100,8 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The <see cref="IBeatmap"/> to compute the difficulty for.</param>
|
||||
/// <param name="mods">The <see cref="Mod"/>s that should be applied.</param>
|
||||
/// <param name="timeRate">The rate of time in <paramref name="beatmap"/>.</param>
|
||||
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
|
||||
/// <returns>A structure containing the difficulty attributes.</returns>
|
||||
protected abstract DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate);
|
||||
protected abstract DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate);
|
||||
}
|
||||
}
|
||||
|
@ -5,28 +5,37 @@ using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty.Preprocessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Wraps a <see cref="HitObject"/> and provides additional information to be used for difficulty calculation.
|
||||
/// </summary>
|
||||
public class DifficultyHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Milliseconds elapsed since the <see cref="HitObject.StartTime"/> of the previous <see cref="DifficultyHitObject"/>.
|
||||
/// </summary>
|
||||
public double DeltaTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="HitObject"/> this <see cref="DifficultyHitObject"/> refers to.
|
||||
/// The <see cref="HitObject"/> this <see cref="DifficultyHitObject"/> wraps.
|
||||
/// </summary>
|
||||
public readonly HitObject BaseObject;
|
||||
|
||||
/// <summary>
|
||||
/// The previous <see cref="HitObject"/> to <see cref="BaseObject"/>.
|
||||
/// The last <see cref="HitObject"/> which occurs before <see cref="BaseObject"/>.
|
||||
/// </summary>
|
||||
public readonly HitObject LastObject;
|
||||
|
||||
public DifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate)
|
||||
/// <summary>
|
||||
/// Amount of time elapsed between <see cref="BaseObject"/> and <see cref="LastObject"/>.
|
||||
/// </summary>
|
||||
public readonly double DeltaTime;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="DifficultyHitObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="hitObject">The <see cref="HitObject"/> which this <see cref="DifficultyHitObject"/> wraps.</param>
|
||||
/// <param name="lastObject">The last <see cref="HitObject"/> which occurs before <paramref name="hitObject"/> in the beatmap.</param>
|
||||
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
|
||||
public DifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate)
|
||||
{
|
||||
BaseObject = hitObject;
|
||||
LastObject = lastObject;
|
||||
DeltaTime = (hitObject.StartTime - lastObject.StartTime) / timeRate;
|
||||
DeltaTime = (hitObject.StartTime - lastObject.StartTime) / clockRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
/// <summary>
|
||||
/// <see cref="DifficultyHitObject"/>s that were processed previously. They can affect the strain values of the following objects.
|
||||
/// </summary>
|
||||
protected readonly History<DifficultyHitObject> Previous = new History<DifficultyHitObject>(2); // Contained objects not used yet
|
||||
protected readonly LimitedCapacityStack<DifficultyHitObject> Previous = new LimitedCapacityStack<DifficultyHitObject>(2); // Contained objects not used yet
|
||||
|
||||
private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap.
|
||||
private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section.
|
||||
|
@ -8,11 +8,13 @@ using System.Collections.Generic;
|
||||
namespace osu.Game.Rulesets.Difficulty.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full.
|
||||
/// Indexing starts at the top of the stack.
|
||||
/// An indexed stack with limited depth. Indexing starts at the top of the stack.
|
||||
/// </summary>
|
||||
public class History<T> : IEnumerable<T>
|
||||
public class LimitedCapacityStack<T> : IEnumerable<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of elements in the stack.
|
||||
/// </summary>
|
||||
public int Count { get; private set; }
|
||||
|
||||
private readonly T[] array;
|
||||
@ -20,10 +22,10 @@ namespace osu.Game.Rulesets.Difficulty.Utils
|
||||
private int marker; // Marks the position of the most recently added item.
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the History class that is empty and has the specified capacity.
|
||||
/// Constructs a new <see cref="LimitedCapacityStack{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The number of items the History can hold.</param>
|
||||
public History(int capacity)
|
||||
/// <param name="capacity">The number of items the stack can hold.</param>
|
||||
public LimitedCapacityStack(int capacity)
|
||||
{
|
||||
if (capacity < 0)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
@ -34,8 +36,9 @@ namespace osu.Game.Rulesets.Difficulty.Utils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The most recently added item is returned at index 0.
|
||||
/// Retrieves the item at an index in the stack.
|
||||
/// </summary>
|
||||
/// <param name="i">The index of the item to retrieve. The top of the stack is returned at index 0.</param>
|
||||
public T this[int i]
|
||||
{
|
||||
get
|
||||
@ -52,11 +55,12 @@ namespace osu.Game.Rulesets.Difficulty.Utils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item as the most recent one in the history.
|
||||
/// The oldest item is disposed if the history is full.
|
||||
/// Pushes an item to this <see cref="LimitedCapacityStack{T}"/>.
|
||||
/// </summary>
|
||||
public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition.
|
||||
/// <param name="item">The item to push.</param>
|
||||
public void Push(T item)
|
||||
{
|
||||
// Overwrite the oldest item instead of shifting every item by one with every addition.
|
||||
if (marker == 0)
|
||||
marker = capacity - 1;
|
||||
else
|
@ -22,7 +22,8 @@ namespace osu.Game.Rulesets
|
||||
{
|
||||
AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve;
|
||||
|
||||
foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll"))
|
||||
foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll")
|
||||
.Where(f => !Path.GetFileName(f).Contains("Tests")))
|
||||
loadRulesetFromFile(file);
|
||||
}
|
||||
|
||||
@ -124,7 +125,7 @@ namespace osu.Game.Rulesets
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e, "Failed to load ruleset");
|
||||
Logger.Error(e, $"Failed to load ruleset {filename}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user