mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 15:44:04 +09:00
Merge remote-tracking branch 'Joehuu/master' into use-lifetime-optimization
This commit is contained in:
@ -7,8 +7,13 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
public class DifficultyAttributes
|
||||
{
|
||||
public readonly Mod[] Mods;
|
||||
public readonly double StarRating;
|
||||
public Mod[] Mods;
|
||||
|
||||
public double StarRating;
|
||||
|
||||
public DifficultyAttributes()
|
||||
{
|
||||
}
|
||||
|
||||
public DifficultyAttributes(Mod[] mods, double starRating)
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
@ -7,12 +7,20 @@ using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
public abstract class DifficultyCalculator
|
||||
{
|
||||
/// <summary>
|
||||
/// The length of each strain section.
|
||||
/// </summary>
|
||||
protected virtual int SectionLength => 400;
|
||||
|
||||
private readonly Ruleset ruleset;
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
|
||||
@ -35,7 +43,7 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
var clock = new StopwatchClock();
|
||||
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
|
||||
|
||||
return Calculate(playableBeatmap, mods, clock.Rate);
|
||||
return calculate(playableBeatmap, mods, clock.Rate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -53,6 +61,44 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
}
|
||||
}
|
||||
|
||||
private DifficultyAttributes calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||
{
|
||||
var skills = CreateSkills(beatmap);
|
||||
|
||||
if (!beatmap.HitObjects.Any())
|
||||
return CreateDifficultyAttributes(beatmap, mods, skills, clockRate);
|
||||
|
||||
var difficultyHitObjects = CreateDifficultyHitObjects(beatmap, clockRate).OrderBy(h => h.BaseObject.StartTime).ToList();
|
||||
|
||||
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;
|
||||
|
||||
foreach (DifficultyHitObject h in difficultyHitObjects)
|
||||
{
|
||||
while (h.BaseObject.StartTime > currentSectionEnd)
|
||||
{
|
||||
foreach (Skill s in skills)
|
||||
{
|
||||
s.SaveCurrentPeak();
|
||||
s.StartNewSectionFrom(currentSectionEnd);
|
||||
}
|
||||
|
||||
currentSectionEnd += sectionLength;
|
||||
}
|
||||
|
||||
foreach (Skill s in skills)
|
||||
s.Process(h);
|
||||
}
|
||||
|
||||
// The peak strain will not be saved for the last section in the above loop
|
||||
foreach (Skill s in skills)
|
||||
s.SaveCurrentPeak();
|
||||
|
||||
return CreateDifficultyAttributes(beatmap, mods, skills, clockRate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates all <see cref="Mod"/> combinations which adjust the <see cref="Beatmap"/> difficulty.
|
||||
/// </summary>
|
||||
@ -96,12 +142,27 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty<Mod>();
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the difficulty of a <see cref="Beatmap"/> using a specific <see cref="Mod"/> combination.
|
||||
/// Creates <see cref="DifficultyAttributes"/> to describe beatmap's calculated 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>
|
||||
/// <returns>A structure containing the difficulty attributes.</returns>
|
||||
protected abstract DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate);
|
||||
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty was calculated.</param>
|
||||
/// <param name="mods">The <see cref="Mod"/>s that difficulty was calculated with.</param>
|
||||
/// <param name="skills">The skills which processed the beatmap.</param>
|
||||
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
|
||||
protected abstract DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, 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="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 clockRate);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="Skill"/>s to calculate the difficulty of an <see cref="IBeatmap"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty will be calculated.</param
|
||||
/// <returns>The <see cref="Skill"/>s.</returns>
|
||||
protected abstract Skill[] CreateSkills(IBeatmap beatmap);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.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>
|
||||
/// The <see cref="HitObject"/> this <see cref="DifficultyHitObject"/> wraps.
|
||||
/// </summary>
|
||||
public readonly HitObject BaseObject;
|
||||
|
||||
/// <summary>
|
||||
/// The last <see cref="HitObject"/> which occurs before <see cref="BaseObject"/>.
|
||||
/// </summary>
|
||||
public readonly HitObject LastObject;
|
||||
|
||||
/// <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) / clockRate;
|
||||
}
|
||||
}
|
||||
}
|
109
osu.Game/Rulesets/Difficulty/Skills/Skill.cs
Normal file
109
osu.Game/Rulesets/Difficulty/Skills/Skill.cs
Normal file
@ -0,0 +1,109 @@
|
||||
// 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 osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty.Skills
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to processes strain values of <see cref="DifficultyHitObject"/>s, keep track of strain levels caused by the processed objects
|
||||
/// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
|
||||
/// </summary>
|
||||
public abstract class Skill
|
||||
{
|
||||
/// <summary>
|
||||
/// The peak strain for each <see cref="DifficultyCalculator.SectionLength"/> section of the beatmap.
|
||||
/// </summary>
|
||||
public IList<double> StrainPeaks => strainPeaks;
|
||||
|
||||
/// <summary>
|
||||
/// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other.
|
||||
/// </summary>
|
||||
protected abstract double SkillMultiplier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines how quickly strain decays for the given skill.
|
||||
/// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second.
|
||||
/// </summary>
|
||||
protected abstract double StrainDecayBase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The weight by which each strain value decays.
|
||||
/// </summary>
|
||||
protected virtual double DecayWeight => 0.9;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="DifficultyHitObject"/>s that were processed previously. They can affect the strain values of the following objects.
|
||||
/// </summary>
|
||||
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.
|
||||
|
||||
private readonly List<double> strainPeaks = new List<double>();
|
||||
|
||||
/// <summary>
|
||||
/// Process a <see cref="DifficultyHitObject"/> and update current strain values accordingly.
|
||||
/// </summary>
|
||||
public void Process(DifficultyHitObject current)
|
||||
{
|
||||
currentStrain *= strainDecay(current.DeltaTime);
|
||||
currentStrain += StrainValueOf(current) * SkillMultiplier;
|
||||
|
||||
currentSectionPeak = Math.Max(currentStrain, currentSectionPeak);
|
||||
|
||||
Previous.Push(current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty.
|
||||
/// </summary>
|
||||
public void SaveCurrentPeak()
|
||||
{
|
||||
if (Previous.Count > 0)
|
||||
strainPeaks.Add(currentSectionPeak);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the initial strain level for a new section.
|
||||
/// </summary>
|
||||
/// <param name="offset">The beginning of the new section in milliseconds.</param>
|
||||
public void StartNewSectionFrom(double offset)
|
||||
{
|
||||
// The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries.
|
||||
// This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level.
|
||||
if (Previous.Count > 0)
|
||||
currentSectionPeak = currentStrain * strainDecay(offset - Previous[0].BaseObject.StartTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the calculated difficulty value representing all processed <see cref="DifficultyHitObject"/>s.
|
||||
/// </summary>
|
||||
public double DifficultyValue()
|
||||
{
|
||||
strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
||||
|
||||
double difficulty = 0;
|
||||
double weight = 1;
|
||||
|
||||
// Difficulty is the weighted sum of the highest strains from every section.
|
||||
foreach (double strain in strainPeaks)
|
||||
{
|
||||
difficulty += strain * weight;
|
||||
weight *= DecayWeight;
|
||||
}
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the strain value of a <see cref="DifficultyHitObject"/>. This value is affected by previously processed objects.
|
||||
/// </summary>
|
||||
protected abstract double StrainValueOf(DifficultyHitObject current);
|
||||
|
||||
private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
|
||||
}
|
||||
}
|
90
osu.Game/Rulesets/Difficulty/Utils/LimitedCapacityStack.cs
Normal file
90
osu.Game/Rulesets/Difficulty/Utils/LimitedCapacityStack.cs
Normal file
@ -0,0 +1,90 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// An indexed stack with limited depth. Indexing starts at the top of the stack.
|
||||
/// </summary>
|
||||
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;
|
||||
private readonly int capacity;
|
||||
private int marker; // Marks the position of the most recently added item.
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="LimitedCapacityStack{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The number of items the stack can hold.</param>
|
||||
public LimitedCapacityStack(int capacity)
|
||||
{
|
||||
if (capacity < 0)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
this.capacity = capacity;
|
||||
array = new T[capacity];
|
||||
marker = capacity; // Set marker to the end of the array, outside of the indexed range by one.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
{
|
||||
if (i < 0 || i > Count - 1)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
i += marker;
|
||||
if (i > capacity - 1)
|
||||
i -= capacity;
|
||||
|
||||
return array[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes an item to this <see cref="LimitedCapacityStack{T}"/>.
|
||||
/// </summary>
|
||||
/// <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
|
||||
--marker;
|
||||
|
||||
array[marker] = item;
|
||||
|
||||
if (Count < capacity)
|
||||
++Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator which enumerates items in the history starting from the most recently added one.
|
||||
/// </summary>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
for (int i = marker; i < capacity; ++i)
|
||||
yield return array[i];
|
||||
|
||||
if (Count == capacity)
|
||||
for (int i = 0; i < marker; ++i)
|
||||
yield return array[i];
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
|
@ -4,7 +4,7 @@
|
||||
using System;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
|
@ -58,10 +58,9 @@ namespace osu.Game.Rulesets.Judgements
|
||||
Child = new SkinnableDrawable($"Play/{Result.Type}", _ => JudgementText = new OsuSpriteText
|
||||
{
|
||||
Text = Result.Type.GetDescription().ToUpperInvariant(),
|
||||
Font = @"Venera",
|
||||
Font = OsuFont.Numeric.With(size: 12),
|
||||
Colour = judgementColour(Result.Type),
|
||||
Scale = new Vector2(0.85f, 1),
|
||||
TextSize = 12
|
||||
}, restrictSize: false)
|
||||
};
|
||||
}
|
||||
|
@ -58,5 +58,7 @@ namespace osu.Game.Rulesets.Judgements
|
||||
/// <param name="result">The <see cref="JudgementResult"/> to find the numeric health increase for.</param>
|
||||
/// <returns>The numeric health increase of <paramref name="result"/>.</returns>
|
||||
public double HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type);
|
||||
|
||||
public override string ToString() => $"AffectsCombo:{AffectsCombo} MaxResult:{MaxResult} MaxScore:{MaxNumericResult}";
|
||||
}
|
||||
}
|
||||
|
@ -55,5 +55,7 @@ namespace osu.Game.Rulesets.Judgements
|
||||
{
|
||||
Judgement = judgement;
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Type} (Score:{Judgement.NumericResultFor(this)} HP:{Judgement.HealthIncreaseFor(this)} {Judgement})";
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.OpenGL.Vertices;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void OnComboChange(int newCombo);
|
||||
protected abstract void OnComboChange(ValueChangedEvent<int> e);
|
||||
|
||||
protected abstract string FragmentShader { get; }
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public virtual void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
{
|
||||
foreach (var d in drawables.Skip(IncreaseFirstObjectVisibility ? 1 : 0))
|
||||
foreach (var d in drawables.Skip(IncreaseFirstObjectVisibility.Value ? 1 : 0))
|
||||
d.ApplyCustomUpdateState += ApplyHiddenState;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Audio;
|
||||
@ -124,14 +124,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
State.ValueChanged += state =>
|
||||
State.ValueChanged += armed =>
|
||||
{
|
||||
UpdateState(state);
|
||||
UpdateState(armed.NewValue);
|
||||
|
||||
// apply any custom state overrides
|
||||
ApplyCustomUpdateState?.Invoke(this, state);
|
||||
ApplyCustomUpdateState?.Invoke(this, armed.NewValue);
|
||||
|
||||
if (State == ArmedState.Hit)
|
||||
if (armed.NewValue == ArmedState.Hit)
|
||||
PlaySamples();
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -168,11 +168,11 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// </summary>
|
||||
public virtual void PopulateScore(ScoreInfo score)
|
||||
{
|
||||
score.TotalScore = (int)Math.Round(TotalScore);
|
||||
score.Combo = Combo;
|
||||
score.MaxCombo = HighestCombo;
|
||||
score.Accuracy = Math.Round(Accuracy, 4);
|
||||
score.Rank = Rank;
|
||||
score.TotalScore = (long)Math.Round(TotalScore.Value);
|
||||
score.Combo = Combo.Value;
|
||||
score.MaxCombo = HighestCombo.Value;
|
||||
score.Accuracy = Math.Round(Accuracy.Value, 4);
|
||||
score.Rank = Rank.Value;
|
||||
score.Date = DateTimeOffset.Now;
|
||||
|
||||
var hitWindows = CreateHitWindows();
|
||||
@ -298,8 +298,8 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// <param name="result">The <see cref="JudgementResult"/> to apply.</param>
|
||||
protected virtual void ApplyResult(JudgementResult result)
|
||||
{
|
||||
result.ComboAtJudgement = Combo;
|
||||
result.HighestComboAtJudgement = HighestCombo;
|
||||
result.ComboAtJudgement = Combo.Value;
|
||||
result.HighestComboAtJudgement = HighestCombo.Value;
|
||||
|
||||
JudgedHits++;
|
||||
|
||||
@ -371,10 +371,10 @@ namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
default:
|
||||
case ScoringMode.Standardised:
|
||||
return max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo / maxHighestCombo) + bonusScore;
|
||||
return max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo.Value / maxHighestCombo) + bonusScore;
|
||||
case ScoringMode.Classic:
|
||||
// should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1)
|
||||
return bonusScore + baseScore * (1 + Math.Max(0, HighestCombo - 1) / 25);
|
||||
return bonusScore + baseScore * (1 + Math.Max(0, HighestCombo.Value - 1) / 25);
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,7 +389,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
if (storeResults)
|
||||
{
|
||||
MaxHits = JudgedHits;
|
||||
maxHighestCombo = HighestCombo;
|
||||
maxHighestCombo = HighestCombo.Value;
|
||||
maxBaseScore = baseScore;
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,8 @@ using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
@ -13,7 +13,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Configuration;
|
||||
@ -96,10 +96,10 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
IsPaused.ValueChanged += paused =>
|
||||
{
|
||||
if (HasReplayLoaded)
|
||||
if (HasReplayLoaded.Value)
|
||||
return;
|
||||
|
||||
KeyBindingInputManager.UseParentInput = !paused;
|
||||
KeyBindingInputManager.UseParentInput = !paused.NewValue;
|
||||
};
|
||||
|
||||
Cursor = CreateCursor();
|
||||
|
@ -1,9 +1,10 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
@ -121,6 +122,8 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
private const int max_catch_up_updates_per_frame = 50;
|
||||
|
||||
private const double sixty_frame_time = 1000.0 / 60;
|
||||
|
||||
public override bool UpdateSubTree()
|
||||
{
|
||||
requireMoreUpdateLoops = true;
|
||||
@ -130,59 +133,64 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
|
||||
{
|
||||
if (!base.UpdateSubTree())
|
||||
return false;
|
||||
updateClock();
|
||||
|
||||
UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat);
|
||||
|
||||
if (isAttached)
|
||||
if (validState)
|
||||
{
|
||||
// When handling replay input, we need to consider the possibility of fast-forwarding, which may cause the clock to be updated
|
||||
// to a point very far into the future, then playing a frame at that time. In such a case, lifetime MUST be updated before
|
||||
// input is handled. This is why base.Update is not called from the derived Update when handling replay input, and is instead
|
||||
// called manually at the correct time here.
|
||||
base.Update();
|
||||
base.UpdateSubTree();
|
||||
UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
private void updateClock()
|
||||
{
|
||||
if (parentClock == null) return;
|
||||
|
||||
clock.Rate = parentClock.Rate;
|
||||
clock.IsRunning = parentClock.IsRunning;
|
||||
|
||||
if (!isAttached)
|
||||
{
|
||||
clock.CurrentTime = parentClock.CurrentTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime);
|
||||
var newProposedTime = parentClock.CurrentTime;
|
||||
|
||||
if (newTime == null)
|
||||
try
|
||||
{
|
||||
if (Math.Abs(clock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
|
||||
{
|
||||
// we shouldn't execute for this time value. probably waiting on more replay data.
|
||||
validState = false;
|
||||
return;
|
||||
newProposedTime = clock.Rate > 0
|
||||
? Math.Min(newProposedTime, clock.CurrentTime + sixty_frame_time)
|
||||
: Math.Max(newProposedTime, clock.CurrentTime - sixty_frame_time);
|
||||
}
|
||||
|
||||
clock.CurrentTime = newTime.Value;
|
||||
if (!isAttached)
|
||||
{
|
||||
clock.CurrentTime = newProposedTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
double? newTime = replayInputHandler.SetFrameFromTime(newProposedTime);
|
||||
|
||||
if (newTime == null)
|
||||
{
|
||||
// we shouldn't execute for this time value. probably waiting on more replay data.
|
||||
validState = false;
|
||||
|
||||
requireMoreUpdateLoops = true;
|
||||
clock.CurrentTime = newProposedTime;
|
||||
return;
|
||||
}
|
||||
|
||||
clock.CurrentTime = newTime.Value;
|
||||
}
|
||||
|
||||
requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
|
||||
}
|
||||
|
||||
requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
|
||||
|
||||
// The manual clock time has changed in the above code. The framed clock now needs to be updated
|
||||
// to ensure that the its time is valid for our children before input is processed
|
||||
Clock.ProcessFrame();
|
||||
|
||||
if (!isAttached)
|
||||
finally
|
||||
{
|
||||
// For non-replay input handling, this provides equivalent input ordering as if Update was not overridden
|
||||
base.Update();
|
||||
// The manual clock time has changed in the above code. The framed clock now needs to be updated
|
||||
// to ensure that the its time is valid for our children before input is processed
|
||||
Clock.ProcessFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,6 +219,7 @@ namespace osu.Game.Rulesets.UI
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return base.Handle(e);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.UI.Scrolling.Algorithms;
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
|
@ -4,7 +4,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Lists;
|
||||
@ -157,10 +157,10 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.IncreaseScrollSpeed:
|
||||
this.TransformBindableTo(TimeRange, TimeRange - time_span_step, 200, Easing.OutQuint);
|
||||
this.TransformBindableTo(TimeRange, TimeRange.Value - time_span_step, 200, Easing.OutQuint);
|
||||
return true;
|
||||
case GlobalAction.DecreaseScrollSpeed:
|
||||
this.TransformBindableTo(TimeRange, TimeRange + time_span_step, 200, Easing.OutQuint);
|
||||
this.TransformBindableTo(TimeRange, TimeRange.Value + time_span_step, 200, Easing.OutQuint);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user