1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 22:07:25 +08:00

Move some common functionality to OsuAutoReplayBase.cs

This commit is contained in:
Thomas Tan 2017-04-28 23:19:15 +08:00
parent 6392fcbc5d
commit e56bd3430a
3 changed files with 153 additions and 88 deletions

View File

@ -14,95 +14,51 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
using osu.Game.Users;
namespace osu.Game.Rulesets.Osu
namespace osu.Game.Rulesets.Osu.Replays
{
public class OsuAutoReplay : Replay
public class OsuAutoReplay : OsuAutoReplayBase
{
private static readonly Vector2 spinner_centre = new Vector2(256, 192);
// Options
private const float spin_radius = 50;
/// <summary>
/// If delayed movements should be used, causing the cursor to stay on each hitobject for as long as possible.
/// Mainly for Autopilot.
/// </summary>
public readonly bool DelayedMovements; // ModManager.CheckActive(Mods.Relax2);
private readonly Beatmap<OsuHitObject> beatmap;
// ms between each ReplayFrame
private readonly float frameDelay;
// Local constants
// ms between 'seeing' a new hitobject and auto moving to 'react' to it
private readonly int reactionTime;
/// <summary>
/// The "reaction time" in ms between "seeing" a new hit object and moving to "react" to it.
/// </summary>
private double reactionTime;
// What easing to use when moving between hitobjects
private EasingTypes preferredEasing;
/// <summary>
/// What easing to use when moving between hitobjects
/// </summary>
private EasingTypes preferredEasing => DelayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out;
/// <summary>
/// Which button (left or right) to use for the current hitobject.
/// Even means LMB will be used to click, odd means RMB will be used.
/// This keeps track of the button previously used for alt/singletap logic.
/// </summary>
// Even means LMB will be used to click, odd means RMB will be used.
// This keeps track of the button previously used for alt/singletap logic.
private int buttonIndex;
public OsuAutoReplay(Beatmap<OsuHitObject> beatmap)
protected override void Initialise()
{
this.beatmap = beatmap;
User = new User
{
Username = @"Autoplay",
};
// We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps.
frameDelay = (float)applyModsToRate(1000.0 / 60.0);
base.Initialise();
// Already superhuman, but still somewhat realistic
reactionTime = (int)applyModsToRate(100);
createAutoReplay();
reactionTime = applyModsToRate(100);
}
private class ReplayFrameComparer : IComparer<ReplayFrame>
{
public int Compare(ReplayFrame f1, ReplayFrame f2)
{
if (f1 == null) throw new NullReferenceException($@"{nameof(f1)} cannot be null");
if (f2 == null) throw new NullReferenceException($@"{nameof(f2)} cannot be null");
return f1.Time.CompareTo(f2.Time);
}
}
private static readonly IComparer<ReplayFrame> replay_frame_comparer = new ReplayFrameComparer();
private int findInsertionIndex(ReplayFrame frame)
{
int index = Frames.BinarySearch(frame, replay_frame_comparer);
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;
}
}
return index;
}
private void addFrameToReplay(ReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame);
private static Vector2 circlePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius));
private double applyModsToTime(double v) => v;
private double applyModsToRate(double v) => v;
public bool DelayedMovements; // ModManager.CheckActive(Mods.Relax2);
private void createAutoReplay()
protected override void CreateAutoReplay()
{
buttonIndex = 0;
preferredEasing = DelayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out;
addFrameToReplay(new ReplayFrame(-100000, 256, 500, ReplayButtonState.None));
addFrameToReplay(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1500, 256, 500, ReplayButtonState.None));
addFrameToReplay(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 256, 192, ReplayButtonState.None));
@ -112,12 +68,6 @@ namespace osu.Game.Rulesets.Osu
{
OsuHitObject h = beatmap.HitObjects[i];
//if (h.EndTime < InputManager.ReplayStartTime)
//{
// h.IsHit = true;
// continue;
//}
if (DelayedMovements && i > 0)
{
OsuHitObject prev = beatmap.HitObjects[i - 1];
@ -126,9 +76,6 @@ namespace osu.Game.Rulesets.Osu
addHitObjectReplay(h);
}
//Player.currentScore.Replay = InputManager.ReplayScore.Replay;
//Player.currentScore.PlayerName = "osu!";
}
private void addDelayedMovements(OsuHitObject h, OsuHitObject prev)
@ -152,7 +99,7 @@ namespace osu.Game.Rulesets.Osu
if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
}
private void addHitObjectReplay(OsuHitObject h)
{
// Default values for circles/sliders
@ -187,7 +134,7 @@ namespace osu.Game.Rulesets.Osu
addHitObjectClickFrames(h, startPosition, spinnerDirection);
}
private static void calcSpinnerStartPosAndDirection(Vector2 prevPos, out Vector2 startPosition, out float spinnerDirection)
{
Vector2 spinCentreOffset = spinner_centre - prevPos;
@ -234,7 +181,7 @@ namespace osu.Game.Rulesets.Osu
spinnerDirection = 1;
}
}
private void moveToHitObject(double targetTime, Vector2 targetPos, double hitObjectRadius, EasingTypes easing)
{
ReplayFrame lastFrame = Frames[Frames.Count - 1];
@ -269,10 +216,10 @@ namespace osu.Game.Rulesets.Osu
{
buttonIndex++;
}
}
// Add frames to click the hitobject
private void addHitObjectClickFrames(OsuHitObject h, Vector2 startPosition, float spinnerDirection)
}
// Add frames to click the hitobject
private void addHitObjectClickFrames(OsuHitObject h, Vector2 startPosition, float spinnerDirection)
{
// Time to insert the first frame which clicks the object
// Here we mainly need to determine which button to use

View File

@ -0,0 +1,114 @@
// 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.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
using osu.Game.Users;
namespace osu.Game.Rulesets.Osu.Replays
{
public abstract class OsuAutoReplayBase : Replay
{
#region Constants
/// <summary>
/// Constants (for spinners).
/// </summary>
protected static readonly Vector2 spinner_centre = new Vector2(256, 192);
protected const float spin_radius = 50;
/// <summary>
/// The beatmap we're making a replay out of.
/// </summary>
protected readonly Beatmap<OsuHitObject> beatmap;
/// <summary>
/// The time in ms between each ReplayFrame.
/// </summary>
protected double frameDelay;
#endregion
#region Construction
public OsuAutoReplayBase(Beatmap<OsuHitObject> beatmap)
{
this.beatmap = beatmap;
Initialise();
CreateAutoReplay();
}
/// <summary>
/// Initialise this instance. Called before CreateAutoReplay.
/// </summary>
protected virtual void Initialise()
{
User = new User
{
Username = @"Autoplay",
};
// We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps.
frameDelay = applyModsToRate(1000.0 / 60.0);
}
/// <summary>
/// Creates the auto replay. Every OsuAutoReplayBase subclass should implement this!
/// </summary>
protected abstract void CreateAutoReplay();
#endregion
#region Utilities
protected double applyModsToTime(double v) => v;
protected double applyModsToRate(double v) => v;
private class ReplayFrameComparer : IComparer<ReplayFrame>
{
public int Compare(ReplayFrame f1, ReplayFrame f2)
{
if (f1 == null) throw new NullReferenceException($@"{nameof(f1)} cannot be null");
if (f2 == null) throw new NullReferenceException($@"{nameof(f2)} cannot be null");
return f1.Time.CompareTo(f2.Time);
}
}
private static readonly IComparer<ReplayFrame> replay_frame_comparer = new ReplayFrameComparer();
protected int findInsertionIndex(ReplayFrame frame)
{
int index = Frames.BinarySearch(frame, replay_frame_comparer);
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;
}
}
return index;
}
protected void addFrameToReplay(ReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame);
protected static Vector2 circlePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius));
#endregion
}
}

View File

@ -69,7 +69,6 @@
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
<Compile Include="Objects\OsuHitObjectDifficulty.cs" />
<Compile Include="Objects\SliderTick.cs" />
<Compile Include="OsuAutoReplay.cs" />
<Compile Include="OsuDifficultyCalculator.cs" />
<Compile Include="OsuKeyConversionInputManager.cs" />
<Compile Include="Scoring\OsuScoreProcessor.cs" />
@ -83,6 +82,8 @@
<Compile Include="Objects\Spinner.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Mods\OsuMod.cs" />
<Compile Include="Replays\OsuAutoReplay.cs" />
<Compile Include="Replays\OsuAutoReplayBase.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
@ -102,6 +103,9 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Folder Include="Replays\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.