Skip to content

Commit

Permalink
Merge pull request #112 from OliBomby/autofail-detector
Browse files Browse the repository at this point in the history
Autofail detector
  • Loading branch information
OliBomby authored Aug 29, 2020
2 parents b3c4d61 + dd853a7 commit ffe927e
Show file tree
Hide file tree
Showing 34 changed files with 2,188 additions and 59 deletions.
102 changes: 91 additions & 11 deletions Mapping Tools/Classes/BeatmapHelper/Beatmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ public class Beatmap : ITextFile {
/// </summary>
public List<double> Bookmarks { get => GetBookmarks(); set => SetBookmarks(value); }

/// <summary>
/// When true, all coordinates and times will be serialized without rounding.
/// </summary>
public bool SaveWithFloatPrecision { get; set; }

/// <summary>
/// Initializes the Beatmap file format.
/// </summary>
Expand Down Expand Up @@ -245,15 +250,20 @@ public void SetLines(List<string> lines) {
/// Sorts all hitobjects in map by order of time.
/// </summary>
public void SortHitObjects() {
HitObjects = HitObjects.OrderBy(o => o.Time).ToList();
HitObjects.Sort();
}

/// <summary>
/// Calculates the temporal length for all <see cref="HitObject"/> sliders and stores it to their internal property.
/// </summary>
public void CalculateSliderEndTimes() {
foreach (var ho in HitObjects.Where(ho => ho.IsSlider)) {
ho.TemporalLength = BeatmapTiming.CalculateSliderTemporalLength(ho.Time, ho.PixelLength);
if (double.IsNaN(ho.PixelLength) || ho.PixelLength < 0 || ho.CurvePoints.All(o => o == ho.Pos)) {
ho.TemporalLength = 0;
}
else {
ho.TemporalLength = BeatmapTiming.CalculateSliderTemporalLength(ho.Time, ho.PixelLength);
}
}
}

Expand Down Expand Up @@ -426,7 +436,10 @@ public List<string> GetLines() {
lines.AddRange(StoryboardSoundSamples.Select(sbss => sbss.GetLine()));
lines.Add("");
lines.Add("[TimingPoints]");
lines.AddRange(from tp in BeatmapTiming.TimingPoints where tp != null select tp.GetLine());
lines.AddRange(BeatmapTiming.TimingPoints.Where(tp => tp != null).Select(tp => {
tp.SaveWithFloatPrecision = SaveWithFloatPrecision;
return tp.GetLine();
}));
lines.Add("");
if (ComboColours.Any()) {
lines.Add("");
Expand All @@ -440,20 +453,68 @@ public List<string> GetLines() {
}
lines.Add("");
lines.Add("[HitObjects]");
lines.AddRange(HitObjects.Select(ho => ho.GetLine()));
lines.AddRange(HitObjects.Select(ho => {
ho.SaveWithFloatPrecision = SaveWithFloatPrecision;
return ho.GetLine();
}));

return lines;
}

public double GetHitObjectStartTime() {
return HitObjects.Min(h => h.Time);
}

public double GetHitObjectEndTime() {
return HitObjects.Max(h => h.EndTime);
}

public void OffsetTime(double offset) {
BeatmapTiming.TimingPoints?.ForEach(tp => tp.Offset += offset);
HitObjects?.ForEach(h => h.MoveTime(offset));
}

private IEnumerable<Event> EnumerateAllEvents() {
return BackgroundAndVideoEvents.Concat(BreakPeriods).Concat(StoryboardSoundSamples)
.Concat(StoryboardLayerFail).Concat(StoryboardLayerPass).Concat(StoryboardLayerBackground)
.Concat(StoryboardLayerForeground).Concat(StoryboardLayerOverlay);
}

public double GetLeadInTime() {
var leadInTime = General["AudioLeadIn"].DoubleValue;
var od = Difficulty["OverallDifficulty"].DoubleValue;
var window50 = Math.Ceiling(200 - 10 * od);
var eventsWithStartTime = EnumerateAllEvents().OfType<IHasStartTime>().ToArray();
if (eventsWithStartTime.Length > 0)
leadInTime = Math.Max(-eventsWithStartTime.Min(o => o.StartTime), leadInTime);
if (HitObjects.Count > 0) {
var approachTime = ApproachRateToMs(Difficulty["ApproachRate"].DoubleValue);
leadInTime = Math.Max(approachTime - HitObjects[0].Time, leadInTime);
}
return leadInTime + window50 + 1000;
}

public double GetMapStartTime() {
return -GetLeadInTime();
}

public double GetMapEndTime() {
var endTime = HitObjects.Count > 0
? Math.Max(GetHitObjectEndTime() + 200, HitObjects.Last().EndTime + 3000)
: double.NegativeInfinity;
var eventsWithEndTime = EnumerateAllEvents().OfType<IHasEndTime>().ToArray();
if (eventsWithEndTime.Length > 0)
endTime = Math.Max(endTime, eventsWithEndTime.Max(o => o.EndTime) - 500);
return endTime;
}

/// <summary>
/// Grabs the specified file name of beatmap file.
/// with format of:
/// <c>Artist - Title (Host) [Difficulty].osu</c>
/// Gets the time at which auto-fail gets checked by osu!
/// The counted judgements must add up to the object count at this time.
/// </summary>
/// <returns>String of file name.</returns>
public string GetFileName() {
return GetFileName(Metadata["Artist"].Value, Metadata["Title"].Value,
Metadata["Creator"].Value, Metadata["Version"].Value);
/// <returns></returns>
public double GetAutoFailCheckTime() {
return GetHitObjectEndTime() + 200;
}

/// <summary>
Expand Down Expand Up @@ -502,6 +563,17 @@ public IEnumerable<HitObject> QueryTimeCode(string code) {
}
}

/// <summary>
/// Grabs the specified file name of beatmap file.
/// with format of:
/// <c>Artist - Title (Host) [Difficulty].osu</c>
/// </summary>
/// <returns>String of file name.</returns>
public string GetFileName() {
return GetFileName(Metadata["Artist"].Value, Metadata["Title"].Value,
Metadata["Creator"].Value, Metadata["Version"].Value);
}

/// <summary>
/// Grabs the specified file name of beatmap file.
/// with format of:
Expand Down Expand Up @@ -554,5 +626,13 @@ private static List<string> GetCategoryLines(List<string> lines, string category
}
return categoryLines;
}

public Beatmap DeepCopy() {
var newBeatmap = (Beatmap)MemberwiseClone();
newBeatmap.HitObjects = HitObjects?.Select(h => h.DeepCopy()).ToList();
newBeatmap.BeatmapTiming = new Timing(BeatmapTiming.TimingPoints.Select(t => t.Copy()).ToList(), BeatmapTiming.SliderMultiplier);
newBeatmap.GiveObjectsGreenlines();
return newBeatmap;
}
}
}
3 changes: 3 additions & 0 deletions Mapping Tools/Classes/BeatmapHelper/ComboColour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public ComboColour(string line) {
Color = Color.FromRgb((byte)r, (byte)g, (byte)b);
}

public ComboColour Copy() {
return (ComboColour) MemberwiseClone();
}

/// <summary>Returns a string that represents the current object.</summary>
/// <returns>A string that represents the current object.</returns>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Mapping_Tools.Classes.BeatmapHelper.Events {
/// <example>
/// Sample,56056,0,"soft-hitnormal.wav",30
/// </example>
public class StoryboardSoundSample : Event, IEquatable<StoryboardSoundSample>, IHasStartTime {
public class StoryboardSoundSample : Event, IEquatable<StoryboardSoundSample>, IHasStartTime, IHasEndTime {
/// <summary>
/// The time when this sound event occurs.
/// </summary>
Expand Down Expand Up @@ -93,5 +93,10 @@ public bool Equals(StoryboardSoundSample other) {
FilePath == other.FilePath &&
Volume == other.Volume);
}

public int EndTime {
get => StartTime;
set => StartTime = value;
}
}
}
82 changes: 61 additions & 21 deletions Mapping Tools/Classes/BeatmapHelper/HitObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,7 @@ namespace Mapping_Tools.Classes.BeatmapHelper {
/// <summary>
/// </summary>
[JsonObject(MemberSerialization.OptIn)]
public class HitObject : ITextLine {
public List<TimingPoint> BodyHitsounds = new List<TimingPoint>();
private int _repeat;

// Special combined with timeline
public List<TimelineObject> TimelineObjects = new List<TimelineObject>();
public class HitObject : ITextLine, IComparable<HitObject> {

public HitObject() { }

Expand Down Expand Up @@ -202,6 +197,14 @@ public double EndTime {
set => SetEndTime(value);
} // Includes all repeats

private double GetEndTime() {
return Math.Floor(Time + TemporalLength * Repeat + Precision.DOUBLE_EPSILON);
}

private void SetEndTime(double value) {
TemporalLength = Repeat == 0 ? 0 : (value - Time) / Repeat;
}

// Special combined with greenline
[JsonProperty]
public double SliderVelocity { get; set; }
Expand All @@ -215,6 +218,17 @@ public double EndTime {
[JsonProperty]
public bool IsSelected { get; set; }

public List<TimingPoint> BodyHitsounds = new List<TimingPoint>();
private int _repeat;

// Special combined with timeline
public List<TimelineObject> TimelineObjects = new List<TimelineObject>();

/// <summary>
/// When true, all coordinates and times will be serialized without rounding.
/// </summary>
public bool SaveWithFloatPrecision { get; set; }


/// <inheritdoc />
public void SetLine(string line) {
Expand Down Expand Up @@ -335,17 +349,17 @@ public void SetLine(string line) {
/// <inheritdoc />
public string GetLine() {
var values = new List<string> {
Pos.X.ToRoundInvariant(),
Pos.Y.ToRoundInvariant(),
Time.ToRoundInvariant(),
SaveWithFloatPrecision ? Pos.X.ToInvariant() : Pos.X.ToRoundInvariant(),
SaveWithFloatPrecision ? Pos.Y.ToInvariant() : Pos.Y.ToRoundInvariant(),
SaveWithFloatPrecision ? Time.ToInvariant() : Time.ToRoundInvariant(),
ObjectType.ToInvariant(),
Hitsounds.ToInvariant()
};

if (IsSlider) {
var builder = new StringBuilder();
builder.Append(GetPathTypeString());
foreach (var p in CurvePoints) builder.Append($"|{p.X.ToRoundInvariant()}:{p.Y.ToRoundInvariant()}");
foreach (var p in CurvePoints) builder.Append($"|{(SaveWithFloatPrecision ? p.X.ToInvariant() : p.X.ToRoundInvariant())}:{(SaveWithFloatPrecision ? p.Y.ToInvariant() : p.Y.ToRoundInvariant())}");
values.Add(builder.ToString());
values.Add(Repeat.ToInvariant());
values.Add(PixelLength.ToInvariant());
Expand All @@ -364,7 +378,7 @@ public string GetLine() {
values.Add(Extras);
}
} else if (IsSpinner) {
values.Add(EndTime.ToRoundInvariant());
values.Add(SaveWithFloatPrecision ? EndTime.ToInvariant() : EndTime.ToRoundInvariant());
values.Add(Extras);
} else {
// It's a circle or a hold note
Expand All @@ -375,14 +389,6 @@ public string GetLine() {
return string.Join(",", values);
}

private double GetEndTime() {
return Math.Floor(Time + TemporalLength * Repeat + Precision.DOUBLE_EPSILON);
}

private void SetEndTime(double value) {
TemporalLength = Repeat == 0 ? 0 : (value - Time) / Repeat;
}

/// <summary>
/// </summary>
/// <param name="ob"></param>
Expand Down Expand Up @@ -475,7 +481,6 @@ public List<double> GetAllTloTimes(Timing timing) {
/// <param name="deltaTime"></param>
public void MoveTime(double deltaTime) {
Time += deltaTime;
EndTime += deltaTime;

// Move its timelineobjects
foreach (var tlo in TimelineObjects) tlo.Time += deltaTime;
Expand Down Expand Up @@ -643,7 +648,7 @@ public void SetHitsounds(int hitsounds) {

public string GetExtras() {
if (IsHoldNote)
return string.Join(":", EndTime.ToRoundInvariant(), SampleSet.ToIntInvariant(),
return string.Join(":", SaveWithFloatPrecision ? EndTime.ToInvariant() : EndTime.ToRoundInvariant(), SampleSet.ToIntInvariant(),
AdditionSet.ToIntInvariant(), CustomIndex.ToInvariant(), SampleVolume.ToRoundInvariant(), Filename);
return string.Join(":", SampleSet.ToIntInvariant(), AdditionSet.ToIntInvariant(), CustomIndex.ToInvariant(),
SampleVolume.ToRoundInvariant(), Filename);
Expand Down Expand Up @@ -758,6 +763,34 @@ private string GetPathTypeString() {
}
}

/// <summary>
/// Detects a failure in the slider path algorithm causing a slider to become invisible.
/// </summary>
/// <returns></returns>
public bool IsInvisible() {
return PixelLength != 0 && PixelLength <= 0.0001 ||
double.IsNaN(PixelLength) ||
CurvePoints.All(o => o == Pos);
}

public HitObject DeepCopy() {
var newHitObject = (HitObject) MemberwiseClone();
newHitObject.BodyHitsounds = BodyHitsounds?.Select(o => o.Copy()).ToList();
newHitObject.TimelineObjects = TimelineObjects?.Select(o => o.Copy()).ToList();
newHitObject.CurvePoints = CurvePoints?.Copy();
if (EdgeHitsounds != null)
newHitObject.EdgeHitsounds = new List<int>(EdgeHitsounds);
if (EdgeSampleSets != null)
newHitObject.EdgeSampleSets = new List<SampleSet>(EdgeSampleSets);
if (EdgeAdditionSets != null)
newHitObject.EdgeAdditionSets = new List<SampleSet>(EdgeAdditionSets);
newHitObject.TimingPoint = TimingPoint?.Copy();
newHitObject.HitsoundTimingPoint = HitsoundTimingPoint?.Copy();
newHitObject.UnInheritedTimingPoint = UnInheritedTimingPoint?.Copy();
newHitObject.Colour = Colour?.Copy();
return newHitObject;
}

public void Debug() {
Console.WriteLine(GetLine());
foreach (var tp in BodyHitsounds) {
Expand All @@ -778,5 +811,12 @@ public void Debug() {
Console.WriteLine(@"feno volume: " + tlo.FenoSampleVolume);
}
}

public int CompareTo(HitObject other) {
if (ReferenceEquals(this, other)) return 0;
if (ReferenceEquals(null, other)) return 1;
if (Time == other.Time) return other.NewCombo.CompareTo(NewCombo);
return Time.CompareTo(other.Time);
}
}
}
4 changes: 4 additions & 0 deletions Mapping Tools/Classes/BeatmapHelper/TimelineObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,10 @@ public static string GetFileName(SampleSet sampleSet, Hitsound hitsound, int ind
}
}

public TimelineObject Copy() {
return (TimelineObject) MemberwiseClone();
}

public override string ToString() {
return $"{Time}, {ObjectType}, {Repeat}, {FenoSampleVolume}";
}
Expand Down
Loading

0 comments on commit ffe927e

Please sign in to comment.