2019-01-24 08:43:03 +00:00
|
|
|
|
// 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.
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2018-11-20 07:51:59 +00:00
|
|
|
|
using osuTK;
|
2017-04-28 15:19:15 +00:00
|
|
|
|
using osu.Game.Beatmaps;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2021-01-31 16:56:03 +00:00
|
|
|
|
using System.Linq;
|
2018-11-28 08:20:37 +00:00
|
|
|
|
using osu.Game.Replays;
|
2021-01-31 16:59:35 +00:00
|
|
|
|
using osu.Game.Rulesets.Mods;
|
2018-06-27 03:18:00 +00:00
|
|
|
|
using osu.Game.Rulesets.Osu.UI;
|
2017-04-28 15:19:15 +00:00
|
|
|
|
using osu.Game.Rulesets.Replays;
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
namespace osu.Game.Rulesets.Osu.Replays
|
|
|
|
|
{
|
2019-03-07 09:30:31 +00:00
|
|
|
|
public abstract class OsuAutoGeneratorBase : AutoGenerator
|
2017-04-28 15:19:15 +00:00
|
|
|
|
{
|
|
|
|
|
#region Constants
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constants (for spinners).
|
|
|
|
|
/// </summary>
|
2018-06-27 03:18:00 +00:00
|
|
|
|
protected static readonly Vector2 SPINNER_CENTRE = OsuPlayfield.BASE_SIZE / 2;
|
2019-02-28 04:31:40 +00:00
|
|
|
|
|
2019-08-20 18:17:27 +00:00
|
|
|
|
public const float SPIN_RADIUS = 50;
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
#endregion
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 18:08:48 +00:00
|
|
|
|
#region Construction / Initialisation
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 18:08:48 +00:00
|
|
|
|
protected Replay Replay;
|
|
|
|
|
protected List<ReplayFrame> Frames => Replay.Frames;
|
2021-01-31 16:56:03 +00:00
|
|
|
|
private readonly IReadOnlyList<IApplicableToRate> timeAffectingMods;
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2021-01-31 16:59:35 +00:00
|
|
|
|
protected OsuAutoGeneratorBase(IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
2017-04-28 18:08:48 +00:00
|
|
|
|
: base(beatmap)
|
2017-04-28 16:15:53 +00:00
|
|
|
|
{
|
2018-11-29 04:22:45 +00:00
|
|
|
|
Replay = new Replay();
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2021-01-31 16:56:03 +00:00
|
|
|
|
timeAffectingMods = mods.OfType<IApplicableToRate>().ToList();
|
2017-04-28 15:19:15 +00:00
|
|
|
|
}
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
#endregion
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
#region Utilities
|
2019-02-28 04:31:40 +00:00
|
|
|
|
|
2021-01-31 16:56:03 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the real duration of time between <paramref name="startTime"/> and <paramref name="endTime"/>
|
|
|
|
|
/// after applying rate-affecting mods.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This method should only be used when <paramref name="startTime"/> and <paramref name="endTime"/> are very close.
|
|
|
|
|
/// That is because the track rate might be changing with time,
|
|
|
|
|
/// and the method used here is a rough instantaneous approximation.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <param name="startTime">The start time of the time delta, in original track time.</param>
|
|
|
|
|
/// <param name="endTime">The end time of the time delta, in original track time.</param>
|
|
|
|
|
protected double ApplyModsToTimeDelta(double startTime, double endTime)
|
|
|
|
|
{
|
|
|
|
|
double delta = endTime - startTime;
|
|
|
|
|
|
|
|
|
|
foreach (var mod in timeAffectingMods)
|
|
|
|
|
delta /= mod.ApplyToRate(startTime);
|
|
|
|
|
|
|
|
|
|
return delta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected double ApplyModsToRate(double time, double rate)
|
|
|
|
|
{
|
|
|
|
|
foreach (var mod in timeAffectingMods)
|
|
|
|
|
rate = mod.ApplyToRate(time, rate);
|
|
|
|
|
return rate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Calculates the interval after which the next <see cref="ReplayFrame"/> should be generated,
|
|
|
|
|
/// in milliseconds.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="time">The time of the previous frame.</param>
|
|
|
|
|
protected double GetFrameDelay(double time)
|
|
|
|
|
=> ApplyModsToRate(time, 1000.0 / 60);
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
private class ReplayFrameComparer : IComparer<ReplayFrame>
|
|
|
|
|
{
|
2022-12-16 09:16:26 +00:00
|
|
|
|
public int Compare(ReplayFrame? f1, ReplayFrame? f2)
|
2017-04-28 15:19:15 +00:00
|
|
|
|
{
|
2022-12-22 20:27:59 +00:00
|
|
|
|
ArgumentNullException.ThrowIfNull(f1);
|
|
|
|
|
ArgumentNullException.ThrowIfNull(f2);
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
return f1.Time.CompareTo(f2.Time);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
private static readonly IComparer<ReplayFrame> replay_frame_comparer = new ReplayFrameComparer();
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 18:30:34 +00:00
|
|
|
|
protected int FindInsertionIndex(ReplayFrame frame)
|
2017-04-28 15:19:15 +00:00
|
|
|
|
{
|
|
|
|
|
int index = Frames.BinarySearch(frame, replay_frame_comparer);
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
if (index < 0)
|
|
|
|
|
{
|
|
|
|
|
index = ~index;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Go to the first index which is actually bigger
|
|
|
|
|
while (index < Frames.Count && frame.Time == Frames[index].Time)
|
|
|
|
|
{
|
|
|
|
|
++index;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
return index;
|
|
|
|
|
}
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 18:30:34 +00:00
|
|
|
|
protected void AddFrameToReplay(ReplayFrame frame) => Frames.Insert(FindInsertionIndex(frame), frame);
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 18:30:34 +00:00
|
|
|
|
protected static Vector2 CirclePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius));
|
2018-04-13 09:19:50 +00:00
|
|
|
|
|
2017-04-28 15:19:15 +00:00
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|