mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 17:32:54 +08:00
Merge remote-tracking branch 'refs/remotes/ppy/master' into ingame_options
This commit is contained in:
commit
261f1f4e1f
@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mania.MathUtils;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
||||
using OpenTK;
|
||||
using osu.Game.Audio;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
{
|
||||
@ -161,9 +162,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
pattern.Add(new HoldNote
|
||||
{
|
||||
StartTime = HitObject.StartTime,
|
||||
Samples = HitObject.Samples,
|
||||
Duration = endTimeData.Duration,
|
||||
Column = column,
|
||||
Head = { Samples = sampleInfoListAt(HitObject.StartTime) },
|
||||
Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) },
|
||||
});
|
||||
}
|
||||
else if (positionData != null)
|
||||
@ -178,6 +180,24 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the sample info list at a point in time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
||||
/// <returns></returns>
|
||||
private SampleInfoList sampleInfoListAt(double time)
|
||||
{
|
||||
var curveData = HitObject as IHasCurve;
|
||||
|
||||
if (curveData == null)
|
||||
return HitObject.Samples;
|
||||
|
||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.RepeatCount;
|
||||
|
||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||
return curveData.RepeatSamples[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,5 +9,29 @@ namespace osu.Game.Rulesets.Mania.Judgements
|
||||
/// Whether the hold note has been released too early and shouldn't give full score for the release.
|
||||
/// </summary>
|
||||
public bool HasBroken;
|
||||
|
||||
public override int NumericResultForScore(ManiaHitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return base.NumericResultForScore(result);
|
||||
case ManiaHitResult.Great:
|
||||
case ManiaHitResult.Perfect:
|
||||
return base.NumericResultForScore(HasBroken ? ManiaHitResult.Good : result);
|
||||
}
|
||||
}
|
||||
|
||||
public override int NumericResultForAccuracy(ManiaHitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return base.NumericResultForAccuracy(result);
|
||||
case ManiaHitResult.Great:
|
||||
case ManiaHitResult.Perfect:
|
||||
return base.NumericResultForAccuracy(HasBroken ? ManiaHitResult.Good : result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,5 +5,9 @@ namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
public class HoldNoteTickJudgement : ManiaJudgement
|
||||
{
|
||||
public override bool AffectsCombo => false;
|
||||
|
||||
public override int NumericResultForScore(ManiaHitResult result) => 20;
|
||||
public override int NumericResultForAccuracy(ManiaHitResult result) => 0; // Don't count ticks into accuracy
|
||||
}
|
||||
}
|
@ -2,11 +2,37 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
public class ManiaJudgement : Judgement
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum possible hit result.
|
||||
/// </summary>
|
||||
public const ManiaHitResult MAX_HIT_RESULT = ManiaHitResult.Perfect;
|
||||
|
||||
/// <summary>
|
||||
/// The result value for the combo portion of the score.
|
||||
/// </summary>
|
||||
public int ResultValueForScore => Result == HitResult.Miss ? 0 : NumericResultForScore(ManiaResult);
|
||||
|
||||
/// <summary>
|
||||
/// The result value for the accuracy portion of the score.
|
||||
/// </summary>
|
||||
public int ResultValueForAccuracy => Result == HitResult.Miss ? 0 : NumericResultForAccuracy(ManiaResult);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum result value for the combo portion of the score.
|
||||
/// </summary>
|
||||
public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum result value for the accuracy portion of the score.
|
||||
/// </summary>
|
||||
public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT);
|
||||
|
||||
public override string ResultString => string.Empty;
|
||||
|
||||
public override string MaxResultString => string.Empty;
|
||||
@ -15,5 +41,42 @@ namespace osu.Game.Rulesets.Mania.Judgements
|
||||
/// The hit result.
|
||||
/// </summary>
|
||||
public ManiaHitResult ManiaResult;
|
||||
|
||||
public virtual int NumericResultForScore(ManiaHitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case ManiaHitResult.Bad:
|
||||
return 50;
|
||||
case ManiaHitResult.Ok:
|
||||
return 100;
|
||||
case ManiaHitResult.Good:
|
||||
return 200;
|
||||
case ManiaHitResult.Great:
|
||||
case ManiaHitResult.Perfect:
|
||||
return 300;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int NumericResultForAccuracy(ManiaHitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case ManiaHitResult.Bad:
|
||||
return 50;
|
||||
case ManiaHitResult.Ok:
|
||||
return 100;
|
||||
case ManiaHitResult.Good:
|
||||
return 200;
|
||||
case ManiaHitResult.Great:
|
||||
return 300;
|
||||
case ManiaHitResult.Perfect:
|
||||
return 305;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
osu.Game.Rulesets.Mania/Objects/BarLine.cs
Normal file
21
osu.Game.Rulesets.Mania/Objects/BarLine.cs
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
public class BarLine : ManiaHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The control point which this bar line is part of.
|
||||
/// </summary>
|
||||
public TimingControlPoint ControlPoint;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the beat which this bar line represents within the control point.
|
||||
/// This is a "major" bar line if <see cref="BeatIndex"/> % <see cref="TimingControlPoint.TimeSignature"/> == 0.
|
||||
/// </summary>
|
||||
public int BeatIndex;
|
||||
}
|
||||
}
|
74
osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
Normal file
74
osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// Visualises a <see cref="BarLine"/>. Although this derives DrawableManiaHitObject,
|
||||
/// this does not handle input/sound like a normal hit object.
|
||||
/// </summary>
|
||||
public class DrawableBarLine : DrawableManiaHitObject<BarLine>
|
||||
{
|
||||
/// <summary>
|
||||
/// Height of major bar line triangles.
|
||||
/// </summary>
|
||||
private const float triangle_height = 12;
|
||||
|
||||
/// <summary>
|
||||
/// Offset of the major bar line triangles from the sides of the bar line.
|
||||
/// </summary>
|
||||
private const float triangle_offset = 9;
|
||||
|
||||
public DrawableBarLine(BarLine barLine)
|
||||
: base(barLine)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 1;
|
||||
|
||||
Add(new Box
|
||||
{
|
||||
Name = "Bar line",
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
});
|
||||
|
||||
bool isMajor = barLine.BeatIndex % (int)barLine.ControlPoint.TimeSignature == 0;
|
||||
|
||||
if (isMajor)
|
||||
{
|
||||
Add(new EquilateralTriangle
|
||||
{
|
||||
Name = "Left triangle",
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.TopCentre,
|
||||
Size = new Vector2(triangle_height),
|
||||
X = -triangle_offset,
|
||||
Rotation = 90
|
||||
});
|
||||
|
||||
Add(new EquilateralTriangle
|
||||
{
|
||||
Name = "Right triangle",
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.TopCentre,
|
||||
Size = new Vector2(triangle_height),
|
||||
X = triangle_offset,
|
||||
Rotation = -90
|
||||
});
|
||||
}
|
||||
|
||||
if (!isMajor && barLine.BeatIndex % 2 == 1)
|
||||
Alpha = 0.2f;
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
@ -58,6 +58,9 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
|
||||
|
||||
Head.ApplyDefaults(controlPointInfo, difficulty);
|
||||
Tail.ApplyDefaults(controlPointInfo, difficulty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,8 +1,13 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
@ -10,6 +15,143 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
{
|
||||
internal class ManiaScoreProcessor : ScoreProcessor<ManiaHitObject, ManiaJudgement>
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum score achievable.
|
||||
/// Does _not_ include bonus score - for bonus score see <see cref="bonusScore"/>.
|
||||
/// </summary>
|
||||
private const int max_score = 1000000;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of the score attributed to combo.
|
||||
/// </summary>
|
||||
private const double combo_portion_max = max_score * 0.2;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of the score attributed to accuracy.
|
||||
/// </summary>
|
||||
private const double accuracy_portion_max = max_score * 0.8;
|
||||
|
||||
/// <summary>
|
||||
/// The factor used to determine relevance of combos.
|
||||
/// </summary>
|
||||
private const double combo_base = 4;
|
||||
|
||||
/// <summary>
|
||||
/// The combo value at which hit objects result in the max score possible.
|
||||
/// </summary>
|
||||
private const int combo_relevance_cap = 400;
|
||||
|
||||
/// <summary>
|
||||
/// The hit HP multiplier at OD = 0.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_min = 0.75;
|
||||
|
||||
/// <summary>
|
||||
/// The hit HP multiplier at OD = 0.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_mid = 0.85;
|
||||
|
||||
/// <summary>
|
||||
/// The hit HP multiplier at OD = 0.
|
||||
/// </summary>
|
||||
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>
|
||||
/// The MISS HP multiplier at OD = 0.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_miss_min = 0.5;
|
||||
|
||||
/// <summary>
|
||||
/// The MISS HP multiplier at OD = 5.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_miss_mid = 0.75;
|
||||
|
||||
/// <summary>
|
||||
/// The MISS HP multiplier at OD = 10.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_miss_max = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The default MISS HP increase.
|
||||
/// </summary>
|
||||
private const double hp_increase_miss = -0.125;
|
||||
|
||||
/// <summary>
|
||||
/// The MISS HP multiplier. This is multiplied to the miss hp increase.
|
||||
/// </summary>
|
||||
private double hpMissMultiplier = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The HIT HP multiplier. This is multiplied to hit hp increases.
|
||||
/// </summary>
|
||||
private double hpMultiplier = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The cumulative combo portion of the score.
|
||||
/// </summary>
|
||||
private double comboScore => combo_portion_max * comboPortion / maxComboPortion;
|
||||
|
||||
/// <summary>
|
||||
/// The cumulative accuracy portion of the score.
|
||||
/// </summary>
|
||||
private double accuracyScore => accuracy_portion_max * Math.Pow(Accuracy, 4) * totalHits / maxTotalHits;
|
||||
|
||||
/// <summary>
|
||||
/// The cumulative bonus score.
|
||||
/// This is added on top of <see cref="max_score"/>, thus the total score can exceed <see cref="max_score"/>.
|
||||
/// </summary>
|
||||
private double bonusScore;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="comboPortion"/> achieved by a perfect playthrough.
|
||||
/// </summary>
|
||||
private double maxComboPortion;
|
||||
|
||||
/// <summary>
|
||||
/// The portion of the score dedicated to combo.
|
||||
/// </summary>
|
||||
private double comboPortion;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="totalHits"/> achieved by a perfect playthrough.
|
||||
/// </summary>
|
||||
private int maxTotalHits;
|
||||
|
||||
/// <summary>
|
||||
/// The total hits.
|
||||
/// </summary>
|
||||
private int totalHits;
|
||||
|
||||
public ManiaScoreProcessor()
|
||||
{
|
||||
}
|
||||
@ -19,8 +161,124 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ComputeTargets(Beatmap<ManiaHitObject> beatmap)
|
||||
{
|
||||
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.Difficulty;
|
||||
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
|
||||
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
|
||||
|
||||
while (true)
|
||||
{
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
var holdNote = obj as HoldNote;
|
||||
|
||||
if (obj is Note)
|
||||
{
|
||||
AddJudgement(new ManiaJudgement
|
||||
{
|
||||
Result = HitResult.Hit,
|
||||
ManiaResult = ManiaHitResult.Perfect
|
||||
});
|
||||
}
|
||||
else if (holdNote != null)
|
||||
{
|
||||
// Head
|
||||
AddJudgement(new ManiaJudgement
|
||||
{
|
||||
Result = HitResult.Hit,
|
||||
ManiaResult = ManiaJudgement.MAX_HIT_RESULT
|
||||
});
|
||||
|
||||
// Ticks
|
||||
int tickCount = holdNote.Ticks.Count();
|
||||
for (int i = 0; i < tickCount; i++)
|
||||
{
|
||||
AddJudgement(new HoldNoteTickJudgement
|
||||
{
|
||||
Result = HitResult.Hit,
|
||||
ManiaResult = ManiaJudgement.MAX_HIT_RESULT,
|
||||
});
|
||||
}
|
||||
|
||||
AddJudgement(new HoldNoteTailJudgement
|
||||
{
|
||||
Result = HitResult.Hit,
|
||||
ManiaResult = ManiaJudgement.MAX_HIT_RESULT
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!HasFailed)
|
||||
break;
|
||||
|
||||
hpMultiplier *= 1.01;
|
||||
hpMissMultiplier *= 0.98;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
maxTotalHits = totalHits;
|
||||
maxComboPortion = comboPortion;
|
||||
}
|
||||
|
||||
protected override void OnNewJudgement(ManiaJudgement judgement)
|
||||
{
|
||||
bool isTick = judgement is HoldNoteTickJudgement;
|
||||
|
||||
if (!isTick)
|
||||
totalHits++;
|
||||
|
||||
switch (judgement.Result)
|
||||
{
|
||||
case HitResult.Miss:
|
||||
Health.Value += hpMissMultiplier * hp_increase_miss;
|
||||
break;
|
||||
case HitResult.Hit:
|
||||
if (isTick)
|
||||
{
|
||||
Health.Value += hpMultiplier * hp_increase_tick;
|
||||
bonusScore += judgement.ResultValueForScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (judgement.ManiaResult)
|
||||
{
|
||||
case ManiaHitResult.Bad:
|
||||
Health.Value += hpMultiplier * hp_increase_bad;
|
||||
break;
|
||||
case ManiaHitResult.Ok:
|
||||
Health.Value += hpMultiplier * hp_increase_ok;
|
||||
break;
|
||||
case ManiaHitResult.Good:
|
||||
Health.Value += hpMultiplier * hp_increase_good;
|
||||
break;
|
||||
case ManiaHitResult.Great:
|
||||
Health.Value += hpMultiplier * hp_increase_great;
|
||||
break;
|
||||
case ManiaHitResult.Perfect:
|
||||
Health.Value += hpMultiplier * hp_increase_perfect;
|
||||
break;
|
||||
}
|
||||
|
||||
// A factor that is applied to make higher combos more relevant
|
||||
double comboRelevance = Math.Min(Math.Max(0.5, Math.Log(Combo.Value, combo_base)), Math.Log(combo_relevance_cap, combo_base));
|
||||
comboPortion += judgement.ResultValueForScore * comboRelevance;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int scoreForAccuracy = 0;
|
||||
int maxScoreForAccuracy = 0;
|
||||
|
||||
foreach (var j in Judgements)
|
||||
{
|
||||
scoreForAccuracy += j.ResultValueForAccuracy;
|
||||
maxScoreForAccuracy += j.MaxResultValueForAccuracy;
|
||||
}
|
||||
|
||||
Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;
|
||||
TotalScore.Value = comboScore + accuracyScore + bonusScore;
|
||||
}
|
||||
|
||||
protected override void Reset()
|
||||
@ -28,6 +286,10 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
base.Reset();
|
||||
|
||||
Health.Value = 1;
|
||||
|
||||
bonusScore = 0;
|
||||
comboPortion = 0;
|
||||
totalHits = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Beatmaps;
|
||||
@ -85,6 +87,34 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var maniaPlayfield = (ManiaPlayfield)Playfield;
|
||||
|
||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||
|
||||
SortedList<TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
||||
for (int i = 0; i < timingPoints.Count; i++)
|
||||
{
|
||||
TimingControlPoint point = timingPoints[i];
|
||||
|
||||
// Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
|
||||
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;
|
||||
|
||||
int index = 0;
|
||||
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
|
||||
{
|
||||
maniaPlayfield.Add(new DrawableBarLine(new BarLine
|
||||
{
|
||||
StartTime = t,
|
||||
ControlPoint = point,
|
||||
BeatIndex = index
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||
|
||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
||||
|
@ -21,6 +21,7 @@ using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
@ -57,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private readonly FlowContainer<Column> columns;
|
||||
public IEnumerable<Column> Columns => columns.Children;
|
||||
|
||||
private readonly ControlPointContainer barlineContainer;
|
||||
private readonly ControlPointContainer barLineContainer;
|
||||
|
||||
private List<Color4> normalColumnColours = new List<Color4>();
|
||||
private Color4 specialColumnColour;
|
||||
@ -77,35 +78,51 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black
|
||||
},
|
||||
columns = new FillFlowContainer<Column>
|
||||
{
|
||||
Name = "Columns",
|
||||
Name = "Masked elements",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding { Left = 1, Right = 1 },
|
||||
Spacing = new Vector2(1, 0)
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black
|
||||
},
|
||||
columns = new FillFlowContainer<Column>
|
||||
{
|
||||
Name = "Columns",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding { Left = 1, Right = 1 },
|
||||
Spacing = new Vector2(1, 0)
|
||||
}
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
|
||||
Children = new[]
|
||||
{
|
||||
barlineContainer = new ControlPointContainer(timingChanges)
|
||||
barLineContainer = new ControlPointContainer(timingChanges)
|
||||
{
|
||||
Name = "Bar lines",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y
|
||||
// Width is set in the Update method
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -190,6 +207,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.ElementAt(h.HitObject.Column).Add(h);
|
||||
public void Add(DrawableBarLine barline) => barLineContainer.Add(barline);
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
@ -224,7 +242,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max);
|
||||
|
||||
barlineContainer.TimeSpan = value;
|
||||
barLineContainer.TimeSpan = value;
|
||||
Columns.ForEach(c => c.ControlPointContainer.TimeSpan = value);
|
||||
}
|
||||
}
|
||||
@ -234,6 +252,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
TransformTo(() => TimeSpan, newTimeSpan, duration, easing, new TransformTimeSpan());
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
// Due to masking differences, it is not possible to get the width of the columns container automatically
|
||||
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
|
||||
barLineContainer.Width = columns.Width;
|
||||
}
|
||||
|
||||
private class TransformTimeSpan : Transform<double>
|
||||
{
|
||||
public override double CurrentValue
|
||||
|
@ -62,6 +62,7 @@
|
||||
<Compile Include="Judgements\ManiaHitResult.cs" />
|
||||
<Compile Include="Judgements\ManiaJudgement.cs" />
|
||||
<Compile Include="ManiaDifficultyCalculator.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableBarLine.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableHoldNote.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableHoldNoteTick.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableManiaHitObject.cs" />
|
||||
@ -70,6 +71,7 @@
|
||||
<Compile Include="Objects\Drawables\Pieces\NotePiece.cs" />
|
||||
<Compile Include="Objects\Types\IHasColumn.cs" />
|
||||
<Compile Include="Scoring\ManiaScoreProcessor.cs" />
|
||||
<Compile Include="Objects\BarLine.cs" />
|
||||
<Compile Include="Objects\HoldNote.cs" />
|
||||
<Compile Include="Objects\HoldNoteTick.cs" />
|
||||
<Compile Include="Objects\ManiaHitObject.cs" />
|
||||
|
@ -27,6 +27,7 @@ namespace osu.Game.Overlays.Mods
|
||||
public class ModButton : ModButtonEmpty, IHasTooltip
|
||||
{
|
||||
private ModIcon foregroundIcon;
|
||||
private ModIcon backgroundIcon;
|
||||
private readonly SpriteText text;
|
||||
private readonly Container<ModIcon> iconsContainer;
|
||||
private SampleChannel sampleOn, sampleOff;
|
||||
@ -35,38 +36,67 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty;
|
||||
|
||||
private int _selectedIndex = -1;
|
||||
private int selectedIndex
|
||||
private const EasingTypes mod_switch_easing = EasingTypes.InOutSine;
|
||||
private const double mod_switch_duration = 120;
|
||||
|
||||
// A selected index of -1 means not selected.
|
||||
private int selectedIndex = -1;
|
||||
|
||||
protected int SelectedIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return _selectedIndex;
|
||||
return selectedIndex;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == _selectedIndex) return;
|
||||
_selectedIndex = value;
|
||||
if (value == selectedIndex) return;
|
||||
|
||||
int direction = value < selectedIndex ? -1 : 1;
|
||||
bool beforeSelected = Selected;
|
||||
|
||||
Mod modBefore = SelectedMod ?? Mods[0];
|
||||
|
||||
if (value >= Mods.Length)
|
||||
selectedIndex = -1;
|
||||
else if (value < -1)
|
||||
selectedIndex = Mods.Length - 1;
|
||||
else
|
||||
selectedIndex = value;
|
||||
|
||||
Mod modAfter = SelectedMod ?? Mods[0];
|
||||
|
||||
if (beforeSelected != Selected)
|
||||
{
|
||||
_selectedIndex = -1;
|
||||
}
|
||||
else if (value <= -2)
|
||||
{
|
||||
_selectedIndex = Mods.Length - 1;
|
||||
iconsContainer.RotateTo(Selected ? 5f : 0f, 300, EasingTypes.OutElastic);
|
||||
iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, EasingTypes.OutElastic);
|
||||
}
|
||||
|
||||
if (modBefore != modAfter)
|
||||
{
|
||||
const float rotate_angle = 16;
|
||||
|
||||
foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing);
|
||||
backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing);
|
||||
|
||||
backgroundIcon.Icon = modAfter.Icon;
|
||||
using (iconsContainer.BeginDelayedSequence(mod_switch_duration, true))
|
||||
{
|
||||
foregroundIcon.RotateTo(-rotate_angle * direction);
|
||||
foregroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing);
|
||||
|
||||
backgroundIcon.RotateTo(rotate_angle * direction);
|
||||
backgroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing);
|
||||
|
||||
iconsContainer.Schedule(() => displayMod(modAfter));
|
||||
}
|
||||
}
|
||||
|
||||
iconsContainer.RotateTo(Selected ? 5f : 0f, 300, EasingTypes.OutElastic);
|
||||
iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, EasingTypes.OutElastic);
|
||||
foregroundIcon.Highlighted = Selected;
|
||||
|
||||
if (mod != null)
|
||||
displayMod(SelectedMod ?? Mods[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Selected => selectedIndex != -1;
|
||||
|
||||
public bool Selected => SelectedIndex != -1;
|
||||
|
||||
private Color4 selectedColour;
|
||||
public Color4 SelectedColour
|
||||
@ -117,7 +147,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
// the mods from Mod, only multiple if Mod is a MultiMod
|
||||
|
||||
public override Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex);
|
||||
public override Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
@ -142,23 +172,25 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public void SelectNext()
|
||||
{
|
||||
(++selectedIndex == -1 ? sampleOff : sampleOn).Play();
|
||||
(++SelectedIndex == -1 ? sampleOff : sampleOn).Play();
|
||||
Action?.Invoke(SelectedMod);
|
||||
}
|
||||
|
||||
public void SelectPrevious()
|
||||
{
|
||||
(--selectedIndex == -1 ? sampleOff : sampleOn).Play();
|
||||
(--SelectedIndex == -1 ? sampleOff : sampleOn).Play();
|
||||
Action?.Invoke(SelectedMod);
|
||||
}
|
||||
|
||||
public void Deselect()
|
||||
{
|
||||
selectedIndex = -1;
|
||||
SelectedIndex = -1;
|
||||
}
|
||||
|
||||
private void displayMod(Mod mod)
|
||||
{
|
||||
if (backgroundIcon != null)
|
||||
backgroundIcon.Icon = foregroundIcon.Icon;
|
||||
foregroundIcon.Icon = mod.Icon;
|
||||
text.Text = mod.Name;
|
||||
}
|
||||
@ -170,17 +202,17 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
iconsContainer.Add(new[]
|
||||
{
|
||||
new ModIcon(Mods[0])
|
||||
backgroundIcon = new ModIcon(Mods[1])
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.BottomRight,
|
||||
Anchor = Anchor.BottomRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Position = new Vector2(1.5f),
|
||||
},
|
||||
foregroundIcon = new ModIcon(Mods[0])
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.BottomRight,
|
||||
Anchor = Anchor.BottomRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Position = new Vector2(-1.5f),
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user