From e56bd3430a5bcaa452504265eb843316dac7d1a0 Mon Sep 17 00:00:00 2001 From: Thomas Tan Date: Fri, 28 Apr 2017 23:19:15 +0800 Subject: [PATCH] Move some common functionality to OsuAutoReplayBase.cs --- .../{ => Replays}/OsuAutoReplay.cs | 121 +++++------------- .../Replays/OsuAutoReplayBase.cs | 114 +++++++++++++++++ .../osu.Game.Rulesets.Osu.csproj | 6 +- 3 files changed, 153 insertions(+), 88 deletions(-) rename osu.Game.Rulesets.Osu/{ => Replays}/OsuAutoReplay.cs (79%) create mode 100644 osu.Game.Rulesets.Osu/Replays/OsuAutoReplayBase.cs diff --git a/osu.Game.Rulesets.Osu/OsuAutoReplay.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoReplay.cs similarity index 79% rename from osu.Game.Rulesets.Osu/OsuAutoReplay.cs rename to osu.Game.Rulesets.Osu/Replays/OsuAutoReplay.cs index c8abdee7dc..39ed587e8c 100644 --- a/osu.Game.Rulesets.Osu/OsuAutoReplay.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoReplay.cs @@ -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; + /// + /// If delayed movements should be used, causing the cursor to stay on each hitobject for as long as possible. + /// Mainly for Autopilot. + /// + public readonly bool DelayedMovements; // ModManager.CheckActive(Mods.Relax2); - private readonly Beatmap 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; + /// + /// The "reaction time" in ms between "seeing" a new hit object and moving to "react" to it. + /// + private double reactionTime; - // What easing to use when moving between hitobjects - private EasingTypes preferredEasing; + /// + /// What easing to use when moving between hitobjects + /// + private EasingTypes preferredEasing => DelayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out; + + /// + /// 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. + /// - // 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 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 - { - 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 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 diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoReplayBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoReplayBase.cs new file mode 100644 index 0000000000..2122d533fa --- /dev/null +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoReplayBase.cs @@ -0,0 +1,114 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// 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 + + /// + /// Constants (for spinners). + /// + protected static readonly Vector2 spinner_centre = new Vector2(256, 192); + protected const float spin_radius = 50; + + /// + /// The beatmap we're making a replay out of. + /// + protected readonly Beatmap beatmap; + + /// + /// The time in ms between each ReplayFrame. + /// + protected double frameDelay; + + #endregion + + #region Construction + + public OsuAutoReplayBase(Beatmap beatmap) + { + this.beatmap = beatmap; + Initialise(); + CreateAutoReplay(); + } + + /// + /// Initialise this instance. Called before CreateAutoReplay. + /// + 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); + } + + /// + /// Creates the auto replay. Every OsuAutoReplayBase subclass should implement this! + /// + protected abstract void CreateAutoReplay(); + + #endregion + + #region Utilities + protected double applyModsToTime(double v) => v; + protected double applyModsToRate(double v) => v; + + private class ReplayFrameComparer : IComparer + { + 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 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 + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index fcad0061e4..8041dd546d 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -69,7 +69,6 @@ - @@ -83,6 +82,8 @@ + + @@ -102,6 +103,9 @@ + + +