Merge branch 'master' into do_not_use_local_user_for_autoplay_score

This commit is contained in:
Dean Herbert
2019-01-04 13:11:17 +09:00
committed by GitHub
246 changed files with 8032 additions and 3576 deletions

View File

@ -44,5 +44,19 @@ namespace osu.Game.Rulesets.Judgements
/// <param name="result">The <see cref="JudgementResult"/> to find the numeric score representation for.</param>
/// <returns>The numeric score representation of <paramref name="result"/>.</returns>
public int NumericResultFor(JudgementResult result) => NumericResultFor(result.Type);
/// <summary>
/// Retrieves the numeric health increase of a <see cref="HitResult"/>.
/// </summary>
/// <param name="result">The <see cref="HitResult"/> to find the numeric health increase for.</param>
/// <returns>The numeric health increase of <paramref name="result"/>.</returns>
protected virtual double HealthIncreaseFor(HitResult result) => 0;
/// <summary>
/// Retrieves the numeric health increase of a <see cref="JudgementResult"/>.
/// </summary>
/// <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);
}
}

View File

@ -1,6 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
namespace osu.Game.Rulesets.Mods
{
public interface IMod
@ -8,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
/// <summary>
/// The shortened name of this mod.
/// </summary>
[JsonProperty("acronym")]
string Acronym { get; }
}
}

View File

@ -19,6 +19,11 @@ namespace osu.Game.Rulesets.Objects
/// </summary>
public class HitObject
{
/// <summary>
/// A small adjustment to the start time of control points to account for rounding/precision errors.
/// </summary>
private const double control_point_leniency = 1;
/// <summary>
/// The time at which the HitObject starts.
/// </summary>
@ -70,7 +75,7 @@ namespace osu.Game.Rulesets.Objects
ApplyDefaultsToSelf(controlPointInfo, difficulty);
// This is done here since ApplyDefaultsToSelf may be used to determine the end time
SampleControlPoint = controlPointInfo.SamplePointAt((this as IHasEndTime)?.EndTime ?? StartTime);
SampleControlPoint = controlPointInfo.SamplePointAt(((this as IHasEndTime)?.EndTime ?? StartTime) + control_point_leniency);
nestedHitObjects.Clear();
@ -87,7 +92,7 @@ namespace osu.Game.Rulesets.Objects
protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
Kiai = controlPointInfo.EffectPointAt(StartTime).KiaiMode;
Kiai = controlPointInfo.EffectPointAt(StartTime + control_point_leniency).KiaiMode;
if (HitWindows == null)
HitWindows = CreateHitWindows();

View File

@ -22,7 +22,6 @@ namespace osu.Game.Rulesets.Objects
/// <summary>
/// Hit window for a <see cref="HitResult.Perfect"/> result.
/// The user can only achieve receive this result if <see cref="AllowsPerfect"/> is true.
/// </summary>
public double Perfect { get; protected set; }
@ -38,7 +37,6 @@ namespace osu.Game.Rulesets.Objects
/// <summary>
/// Hit window for an <see cref="HitResult.Ok"/> result.
/// The user can only achieve this result if <see cref="AllowsOk"/> is true.
/// </summary>
public double Ok { get; protected set; }
@ -53,14 +51,36 @@ namespace osu.Game.Rulesets.Objects
public double Miss { get; protected set; }
/// <summary>
/// Whether it's possible to achieve a <see cref="HitResult.Perfect"/> result.
/// Retrieves the <see cref="HitResult"/> with the largest hit window that produces a successful hit.
/// </summary>
public bool AllowsPerfect;
/// <returns>The lowest allowed successful <see cref="HitResult"/>.</returns>
protected HitResult LowestSuccessfulHitResult()
{
for (var result = HitResult.Meh; result <= HitResult.Perfect; ++result)
{
if (IsHitResultAllowed(result))
return result;
}
return HitResult.None;
}
/// <summary>
/// Whether it's possible to achieve a <see cref="HitResult.Ok"/> result.
/// Check whether it is possible to achieve the provided <see cref="HitResult"/>.
/// </summary>
public bool AllowsOk;
/// <param name="result">The result type to check.</param>
/// <returns>Whether the <see cref="HitResult"/> can be achieved.</returns>
public virtual bool IsHitResultAllowed(HitResult result)
{
switch (result)
{
case HitResult.Perfect:
case HitResult.Ok:
return false;
default:
return true;
}
}
/// <summary>
/// Sets hit windows with values that correspond to a difficulty parameter.
@ -85,18 +105,11 @@ namespace osu.Game.Rulesets.Objects
{
timeOffset = Math.Abs(timeOffset);
if (AllowsPerfect && timeOffset <= HalfWindowFor(HitResult.Perfect))
return HitResult.Perfect;
if (timeOffset <= HalfWindowFor(HitResult.Great))
return HitResult.Great;
if (timeOffset <= HalfWindowFor(HitResult.Good))
return HitResult.Good;
if (AllowsOk && timeOffset <= HalfWindowFor(HitResult.Ok))
return HitResult.Ok;
if (timeOffset <= HalfWindowFor(HitResult.Meh))
return HitResult.Meh;
if (timeOffset <= HalfWindowFor(HitResult.Miss))
return HitResult.Miss;
for (var result = HitResult.Perfect; result >= HitResult.Miss; --result)
{
if (IsHitResultAllowed(result) && timeOffset <= HalfWindowFor(result))
return result;
}
return HitResult.None;
}
@ -130,10 +143,10 @@ namespace osu.Game.Rulesets.Objects
/// <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.
/// This happens if <paramref name="timeOffset"/> is less than what is required for a <see cref="Meh"/> result.
/// This happens if <paramref name="timeOffset"/> is less than what is required for a <see cref="SuccessfulHitWindow"/> result.
/// </summary>
/// <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>
public bool CanBeHit(double timeOffset) => timeOffset <= HalfWindowFor(HitResult.Meh);
public bool CanBeHit(double timeOffset) => timeOffset <= HalfWindowFor(LowestSuccessfulHitResult());
}
}

View File

@ -2,8 +2,11 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.Extensions.TypeExtensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
@ -57,6 +60,11 @@ namespace osu.Game.Rulesets.Scoring
/// </summary>
public readonly BindableInt Combo = new BindableInt();
/// <summary>
/// Create a <see cref="HitWindows"/> for this processor.
/// </summary>
protected virtual HitWindows CreateHitWindows() => new HitWindows();
/// <summary>
/// The current rank.
/// </summary>
@ -166,7 +174,16 @@ namespace osu.Game.Rulesets.Scoring
score.Accuracy = Math.Round(Accuracy, 4);
score.Rank = Rank;
score.Date = DateTimeOffset.Now;
var hitWindows = CreateHitWindows();
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
score.Statistics[result] = GetStatistic(result);
}
protected abstract int GetStatistic(HitResult result);
public abstract double GetStandardisedScore();
}
public class ScoreProcessor<TObject> : ScoreProcessor
@ -273,6 +290,8 @@ namespace osu.Game.Rulesets.Scoring
updateScore();
}
private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
/// <summary>
/// Applies the score change of a <see cref="JudgementResult"/> to this <see cref="ScoreProcessor"/>.
/// </summary>
@ -284,6 +303,9 @@ namespace osu.Game.Rulesets.Scoring
JudgedHits++;
if (result.Type != HitResult.None)
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
if (result.Judgement.AffectsCombo)
{
switch (result.Type)
@ -340,20 +362,30 @@ namespace osu.Game.Rulesets.Scoring
if (rollingMaxBaseScore != 0)
Accuracy.Value = baseScore / rollingMaxBaseScore;
switch (Mode.Value)
TotalScore.Value = getScore(Mode.Value);
}
private double getScore(ScoringMode mode)
{
switch (mode)
{
default:
case ScoringMode.Standardised:
TotalScore.Value = max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo / maxHighestCombo) + bonusScore;
break;
return max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo / maxHighestCombo) + bonusScore;
case ScoringMode.Classic:
// should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1)
TotalScore.Value = bonusScore + baseScore * (1 + Math.Max(0, HighestCombo - 1) / 25);
break;
return bonusScore + baseScore * (1 + Math.Max(0, HighestCombo - 1) / 25);
}
}
protected override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result);
public override double GetStandardisedScore() => getScore(ScoringMode.Standardised);
protected override void Reset(bool storeResults)
{
scoreResultCounts.Clear();
if (storeResults)
{
MaxHits = JudgedHits;

View File

@ -69,6 +69,11 @@ namespace osu.Game.Rulesets.UI
/// </summary>
public Playfield Playfield => playfield.Value;
/// <summary>
/// Place to put drawables above hit objects but below UI.
/// </summary>
public Container Overlays { get; protected set; }
/// <summary>
/// The cursor provided by this <see cref="RulesetContainer"/>. May be null if no cursor is provided.
/// </summary>
@ -216,7 +221,6 @@ namespace osu.Game.Rulesets.UI
protected override Container<Drawable> Content => content;
private Container content;
private IEnumerable<Mod> mods;
/// <summary>
/// Whether to assume the beatmap passed into this <see cref="RulesetContainer{TObject}"/> is for the current ruleset.
@ -246,17 +250,24 @@ namespace osu.Game.Rulesets.UI
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
KeyBindingInputManager.Add(content = new Container
KeyBindingInputManager.Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
});
AddInternal(KeyBindingInputManager);
KeyBindingInputManager.Add(Playfield);
content = new Container
{
RelativeSizeAxes = Axes.Both,
},
Playfield
};
if (Cursor != null)
KeyBindingInputManager.Add(Cursor);
InternalChildren = new Drawable[]
{
KeyBindingInputManager,
Overlays = new Container { RelativeSizeAxes = Axes.Both }
};
// Apply mods
applyRulesetMods(Mods, config);
@ -331,7 +342,6 @@ namespace osu.Game.Rulesets.UI
Playfield.Add(drawableObject);
}
/// <summary>
/// Creates a DrawableHitObject from a HitObject.
/// </summary>