diff --git a/PracticePlugin/Behavior.cs b/PracticePlugin/Behavior.cs new file mode 100644 index 0000000..3ae9274 --- /dev/null +++ b/PracticePlugin/Behavior.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +namespace PracticePlugin +{ + public class Behavior : MonoBehaviour + { + + void Update() + { + if (Plugin._uiElementsCreator == null || UIElementsCreator.SongSeeker == null) return; + UIElementsCreator.SongSeeker.OnUpdate(); + } + } +} diff --git a/PracticePlugin/CustomEffectPoolsInstaller.cs b/PracticePlugin/CustomEffectPoolsInstaller.cs index 94615d1..3327870 100644 --- a/PracticePlugin/CustomEffectPoolsInstaller.cs +++ b/PracticePlugin/CustomEffectPoolsInstaller.cs @@ -1,26 +1,33 @@ -namespace PracticePlugin +using Zenject; +namespace PracticePlugin { - public class CustomEffectPoolsInstaller : EffectPoolsInstaller - { - public override void InstallBindings() - { - Container.BindMemoryPool().WithInitialSize(20).FromComponentInNewPrefab(_flyingTextEffectPrefab); - Container.BindMemoryPool().WithInitialSize(20) - .FromComponentInNewPrefab(_flyingScoreTextEffectPrefab); - Container.BindMemoryPool().WithInitialSize(20) - .FromComponentInNewPrefab(_flyingSpriteEffectPrefab); - Container.BindMemoryPool().WithInitialSize(30).FromComponentInNewPrefab(_noteDebrisPrefab); - Container.BindMemoryPool().WithInitialSize(20).FromComponentInNewPrefab(_beatEffectPrefab); - Container.BindMemoryPool().WithInitialSize(20) - .FromComponentInNewPrefab(_bombCutSoundEffectPrefab);; - - Container.BindMemoryPool().WithInitialSize(10) - .FromComponentInNewPrefab(ReplacePrefab()); - } + /* + public class CustomEffectPoolsInstaller : EffectPoolsInstaller + { + public override void ManualInstallBindings(DiContainer container, bool shortBeatEffect) + { + try + { + container.BindMemoryPool().WithInitialSize(20).FromComponentInNewPrefab(this._flyingTextEffectPrefab); + container.BindMemoryPool().WithInitialSize(20).FromComponentInNewPrefab(this._flyingScoreEffectPrefab); + container.BindMemoryPool().WithInitialSize(20).FromComponentInNewPrefab(this._flyingSpriteEffectPrefab); + container.BindMemoryPool().WithInitialSize(40).FromComponentInNewPrefab(this._noteDebrisHDConditionVariable ? this._noteDebrisHDPrefab : this._noteDebrisLWPrefab); + container.BindMemoryPool().WithInitialSize(20).FromComponentInNewPrefab(shortBeatEffect ? this._shortBeatEffectPrefab : this._beatEffectPrefab); + container.BindMemoryPool().WithInitialSize(16).FromComponentInNewPrefab(this._noteCutSoundEffectPrefab); + container.BindMemoryPool().WithInitialSize(20).FromComponentInNewPrefab(this._bombCutSoundEffectPrefab); + } + catch (System.Exception ex) + { + System.Console.WriteLine(ex.ToString()); + } - private CustomNoteCutSoundEffect ReplacePrefab() - { - return CustomNoteCutSoundEffect.CopyOriginal(_noteCutSoundEffectPrefab); - } - } + } + + private CustomNoteCutSoundEffect ReplacePrefab() + { + return CustomNoteCutSoundEffect.CopyOriginal(_noteCutSoundEffectPrefab); + } + + } +*/ } \ No newline at end of file diff --git a/PracticePlugin/CustomNoteCutSoundEffect.cs b/PracticePlugin/CustomNoteCutSoundEffect.cs index d6499b1..c8703a8 100644 --- a/PracticePlugin/CustomNoteCutSoundEffect.cs +++ b/PracticePlugin/CustomNoteCutSoundEffect.cs @@ -3,43 +3,43 @@ namespace PracticePlugin { - public class CustomNoteCutSoundEffect : NoteCutSoundEffect - { - public static CustomNoteCutSoundEffect CopyOriginal(NoteCutSoundEffect original) - { - var gameObj = Instantiate(original.gameObject); - gameObj.name = "This is a copy!"; - //gameObj.SetActive(false); - original = gameObj.GetComponent(); - var noteCutSoundEffect = (CustomNoteCutSoundEffect) ReflectionUtil.CopyComponent(original, typeof(NoteCutSoundEffect), - typeof(CustomNoteCutSoundEffect), gameObj); - DestroyImmediate(original); - noteCutSoundEffect.Awake(); - return noteCutSoundEffect; - } + public class CustomNoteCutSoundEffect : NoteCutSoundEffect + { + public static CustomNoteCutSoundEffect CopyOriginal(NoteCutSoundEffect original) + { + var gameObj = Instantiate(original.gameObject); + gameObj.name = "This is a copy!"; + //gameObj.SetActive(false); + original = gameObj.GetComponent(); + var noteCutSoundEffect = (CustomNoteCutSoundEffect)ReflectionUtil.CopyComponent(original, typeof(NoteCutSoundEffect), + typeof(CustomNoteCutSoundEffect), gameObj); + DestroyImmediate(original); + noteCutSoundEffect.Awake(); + return noteCutSoundEffect; + } - public override void Awake() - { - if (_badCutSoundEffectAudioClips == null) return; - base.Awake(); - } + public override void Awake() + { + if (_badCutSoundEffectAudioClips == null) return; + base.Awake(); + } - public override void LateUpdate() - { - if (_audioSource.clip == null) return; - base.LateUpdate(); - } + public override void LateUpdate() + { + if (_audioSource.clip == null) return; + base.LateUpdate(); + } - public override void Init(AudioClip audioClip, double noteDSPTime, float aheadTime, float missedTimeOffset, - Saber saber, NoteData noteData, bool handleWrongSaberTypeAsGood) - { - base.Init(audioClip, noteDSPTime, aheadTime, missedTimeOffset, saber, noteData, handleWrongSaberTypeAsGood); - _audioSource.Stop(); - var dspTime = AudioSettings.dspTime; - var timeDiff = noteDSPTime - dspTime; - timeDiff /= Plugin.TimeScale; - var newTime = dspTime + (timeDiff - aheadTime); - _audioSource.PlayScheduled(newTime); - } - } + public override void Init(AudioClip audioClip, double noteDSPTime, float aheadTime, float missedTimeOffset, float timeToPrevNote, float timeToNextNote, + Saber saber, NoteData noteData, bool handleWrongSaberTypeAsGood, float volumeMultiplier, bool ignoreSaberSpeed, bool ignoreBadCuts) + { + base.Init(audioClip, noteDSPTime, aheadTime, missedTimeOffset, timeToPrevNote, timeToNextNote, saber, noteData, handleWrongSaberTypeAsGood, volumeMultiplier, ignoreSaberSpeed, ignoreBadCuts); + _audioSource.Stop(); + var dspTime = AudioSettings.dspTime; + var timeDiff = noteDSPTime - dspTime; + timeDiff /= Plugin.TimeScale; + var newTime = dspTime + (timeDiff - aheadTime); + _audioSource.PlayScheduled(newTime); + } + } } \ No newline at end of file diff --git a/PracticePlugin/LooperUI.cs b/PracticePlugin/LooperUI.cs index 970d95e..20fff80 100644 --- a/PracticePlugin/LooperUI.cs +++ b/PracticePlugin/LooperUI.cs @@ -1,149 +1,173 @@ -using System; +using HMUI; +using System; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; +using BeatSaberMarkupLanguage; namespace PracticePlugin { - public class LooperUI : MonoBehaviour - { - public float StartTime - { - get { return Mathf.InverseLerp(0, SongSeeker.SeekBarSize.x, _startCursor.Position); } - } - - public float EndTime - { - get { return Mathf.InverseLerp(0, SongSeeker.SeekBarSize.x, _endCursor.Position); } - } - - public event Action OnDragEndEvent; - - private static readonly Vector2 CursorSize = new Vector2(3, 3); - - private static readonly Color StartColor = new Color(0.15f, 0.35f, 0.8f, 0.75f); - private static readonly Color EndColor = new Color(0.85f, 0.12f, 0.25f, 0.75f); - private static readonly Color LineDurationColor = new Color(1, 1, 1, 0.4f); - - private const float LineDurationWidth = 1f; - private const float MinCursorDistance = 4f; - private const float StickToSeekerCursorDistance = 2f; - - private static float _prevStartTime; - private static float _prevEndTime = 1f; - - private SongSeeker _songSeeker; - - private Image _lineDuration; - - private LooperCursor _startCursor; - private LooperCursor _endCursor; - - private LooperCursor _draggingCursor; - - private Camera _mainCamera; - - public void Init(SongSeeker songSeeker) - { - _songSeeker = songSeeker; - - if (Plugin.PlayingNewSong) - { - _prevStartTime = 0; - _prevEndTime = 1; - } - - _lineDuration = new GameObject("Line Duration").AddComponent(); - var rectTransform = _lineDuration.rectTransform; - rectTransform.SetParent(transform, false); - rectTransform.anchorMin = Vector2.up * 0.5f; - rectTransform.anchorMax = Vector2.up * 0.5f; - rectTransform.sizeDelta = Vector2.zero; - _lineDuration.color = LineDurationColor; - - var startCursorImage = new GameObject("Start Cursor").AddComponent(); - rectTransform = startCursorImage.rectTransform; - rectTransform.SetParent(transform, false); - rectTransform.anchorMin = Vector2.up * 0.5f; - rectTransform.anchorMax = Vector2.up * 0.5f; - rectTransform.sizeDelta = CursorSize; - rectTransform.localEulerAngles = new Vector3(0, 0, 45); - startCursorImage.color = StartColor; - - _startCursor = startCursorImage.gameObject.AddComponent(); - _startCursor.BeginDragEvent += CursorOnBeginDragEvent; - _startCursor.EndDragEvent += CursorOnEndDragEvent; - _startCursor.Position = Mathf.Lerp(0, SongSeeker.SeekBarSize.x, _prevStartTime); - - var endCursorImage = new GameObject("End Cursor").AddComponent(); - rectTransform = endCursorImage.rectTransform; - rectTransform.SetParent(transform, false); - rectTransform.anchorMin = Vector2.up * 0.5f; - rectTransform.anchorMax = Vector2.up * 0.5f; - rectTransform.sizeDelta = CursorSize; - rectTransform.localEulerAngles = new Vector3(0, 0, 45); - endCursorImage.color = EndColor; - - _endCursor = endCursorImage.gameObject.AddComponent(); - _endCursor.BeginDragEvent += CursorOnBeginDragEvent; - _endCursor.EndDragEvent += CursorOnEndDragEvent; - _endCursor.Position = Mathf.Lerp(0, SongSeeker.SeekBarSize.x, _prevEndTime); - - _startCursor.Init(LooperCursor.Type.Start); - _endCursor.Init(LooperCursor.Type.End); - - _mainCamera = Camera.main; - } - - private void CursorOnBeginDragEvent(LooperCursor cursor, PointerEventData eventData) - { - _draggingCursor = cursor; - } - - private void CursorOnEndDragEvent(LooperCursor cursor, PointerEventData eventData) - { - _draggingCursor = null; - - if (OnDragEndEvent != null) - { - OnDragEndEvent(); - } - } - - private void Update() - { - if (_draggingCursor != null) - { - var eventData = _draggingCursor.EventData; - RectTransformUtility.ScreenPointToLocalPointInRectangle(transform as RectTransform, eventData.position, - _mainCamera, out var pos); - var newPos = pos.x + SongSeeker.HalfSeekBarSize; - - var seekerPos = Mathf.Lerp(0, SongSeeker.SeekBarSize.x, _songSeeker.PlaybackPosition); - if (Mathf.Abs(newPos - seekerPos) <= StickToSeekerCursorDistance) - { - newPos = seekerPos; - } - - if (_draggingCursor.CursorType == LooperCursor.Type.Start) - { - _draggingCursor.Position = Mathf.Clamp(newPos, 0, _endCursor.Position - MinCursorDistance); - } - else - { - _draggingCursor.Position = Mathf.Clamp(newPos, _startCursor.Position + MinCursorDistance, - SongSeeker.SeekBarSize.x); - } - } - - _lineDuration.rectTransform.sizeDelta = new Vector2(_endCursor.Position - _startCursor.Position, LineDurationWidth); - _lineDuration.rectTransform.anchoredPosition = new Vector2((_startCursor.Position + _endCursor.Position) / 2, 0); - } - - private void OnDestroy() - { - _prevStartTime = StartTime; - _prevEndTime = EndTime; - } - } + public class LooperUI : MonoBehaviour + { + public float StartTime + { + get { return Mathf.InverseLerp(0, SongSeeker.SeekBarSize.x, _startCursor.Position); } + } + + public float EndTime + { + get { return Mathf.InverseLerp(0, SongSeeker.SeekBarSize.x, _endCursor.Position); } + } + + public event Action OnDragEndEvent; + + private static readonly Vector2 CursorSize = new Vector2(3, 3); + + private static readonly Color StartColor = new Color(0.15f, 0.35f, 0.8f, 0.75f); + private static readonly Color EndColor = new Color(0.85f, 0.12f, 0.25f, 0.75f); + private static readonly Color LineDurationColor = new Color(1, 1, 1, 0.4f); + + private const float LineDurationWidth = 1f; + private const float MinCursorDistance = 4f; + private const float StickToSeekerCursorDistance = 2f; + + private static float _prevStartTime; + private static float _prevEndTime = 1f; + + private SongSeeker _songSeeker; + + private ImageView _lineDuration; + + private LooperCursor _startCursor; + private LooperCursor _endCursor; + + private LooperCursor _draggingCursor; + + private Camera _mainCamera; + + public void Init(SongSeeker songSeeker) + { + _songSeeker = songSeeker; + + if (Plugin.PlayingNewSong) + { + _prevStartTime = 0; + _prevEndTime = 1; + } + + var tex = Texture2D.whiteTexture; + var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), Vector2.one * 0.5f, 100, 1); + + var bg = new GameObject("Background").AddComponent(); + var rectTransform = bg.rectTransform; + rectTransform.SetParent(transform, false); + rectTransform.sizeDelta = SongSeeker.SeekBarSize + new Vector2(0, 4); + rectTransform.anchoredPosition = new Vector2(0, -1); + bg.sprite = sprite; + bg.type = Image.Type.Simple; + bg.color = new Color(0, 0, 0, 0); + bg.material = Utilities.ImageResources.NoGlowMat; + + _lineDuration = new GameObject("Line Duration").AddComponent(); + rectTransform = _lineDuration.rectTransform; + rectTransform.SetParent(transform, false); + rectTransform.anchorMin = Vector2.up * 0.5f; + rectTransform.anchorMax = Vector2.up * 0.5f; + rectTransform.sizeDelta = Vector2.zero; + _lineDuration.sprite = sprite; + _lineDuration.type = Image.Type.Simple; + _lineDuration.color = LineDurationColor; + _lineDuration.material = Utilities.ImageResources.NoGlowMat; + + var startCursorImage = new GameObject("Start Cursor").AddComponent(); + rectTransform = startCursorImage.rectTransform; + rectTransform.SetParent(transform, false); + rectTransform.anchorMin = Vector2.up * 0.5f; + rectTransform.anchorMax = Vector2.up * 0.5f; + rectTransform.sizeDelta = CursorSize; + rectTransform.localEulerAngles = new Vector3(0, 0, 45); + startCursorImage.sprite = sprite; + startCursorImage.type = Image.Type.Simple; + startCursorImage.color = StartColor; + startCursorImage.material = Utilities.ImageResources.NoGlowMat; + + _startCursor = startCursorImage.gameObject.AddComponent(); + _startCursor.BeginDragEvent += CursorOnBeginDragEvent; + _startCursor.EndDragEvent += CursorOnEndDragEvent; + _startCursor.Position = Mathf.Lerp(0, SongSeeker.SeekBarSize.x, _prevStartTime); + + var endCursorImage = new GameObject("End Cursor").AddComponent(); + rectTransform = endCursorImage.rectTransform; + rectTransform.SetParent(transform, false); + rectTransform.anchorMin = Vector2.up * 0.5f; + rectTransform.anchorMax = Vector2.up * 0.5f; + rectTransform.sizeDelta = CursorSize; + rectTransform.localEulerAngles = new Vector3(0, 0, 45); + endCursorImage.sprite = sprite; + endCursorImage.type = Image.Type.Simple; + endCursorImage.color = EndColor; + endCursorImage.material = Utilities.ImageResources.NoGlowMat; + + _endCursor = endCursorImage.gameObject.AddComponent(); + _endCursor.BeginDragEvent += CursorOnBeginDragEvent; + _endCursor.EndDragEvent += CursorOnEndDragEvent; + _endCursor.Position = Mathf.Lerp(0, SongSeeker.SeekBarSize.x, _prevEndTime); + + _startCursor.Init(LooperCursor.Type.Start); + _endCursor.Init(LooperCursor.Type.End); + + _mainCamera = Camera.main; + } + + private void CursorOnBeginDragEvent(LooperCursor cursor, PointerEventData eventData) + { + _draggingCursor = cursor; + } + + private void CursorOnEndDragEvent(LooperCursor cursor, PointerEventData eventData) + { + _draggingCursor = null; + + if (OnDragEndEvent != null) + { + OnDragEndEvent(); + } + } + + private void Update() + { + if (_draggingCursor != null) + { + var eventData = _draggingCursor.EventData; + bool hovering = (eventData.hovered.Count > 0); + if (!hovering) { return; } + var newPos = Mathf.Lerp(0, SongSeeker.SeekBarSize.x, Mathf.InverseLerp(-1, 1, Mathf.Clamp(eventData.position.x, -1f, 1f))); + + var seekerPos = _songSeeker.PlaybackPosition; + if (Mathf.Abs(newPos - seekerPos) <= StickToSeekerCursorDistance) + { + newPos = seekerPos; + } + + if (_draggingCursor.CursorType == LooperCursor.Type.Start) + { + _draggingCursor.Position = Mathf.Clamp(newPos, 0, _endCursor.Position - MinCursorDistance); + } + else + { + _draggingCursor.Position = Mathf.Clamp(newPos, _startCursor.Position + MinCursorDistance, + SongSeeker.SeekBarSize.x); + } + } + + _lineDuration.rectTransform.sizeDelta = new Vector2(_endCursor.Position - _startCursor.Position, LineDurationWidth); + _lineDuration.rectTransform.anchoredPosition = new Vector2((_startCursor.Position + _endCursor.Position) / 2, 0); + } + + private void OnDestroy() + { + _prevStartTime = StartTime; + _prevEndTime = EndTime; + } + } } \ No newline at end of file diff --git a/PracticePlugin/NoFailGameEnergy.cs b/PracticePlugin/NoFailGameEnergy.cs index fe453a2..ceb6323 100644 --- a/PracticePlugin/NoFailGameEnergy.cs +++ b/PracticePlugin/NoFailGameEnergy.cs @@ -1,58 +1,82 @@ using System.Collections; using System.Linq; using UnityEngine; - namespace PracticePlugin { - public class NoFailGameEnergy : MonoBehaviour - { - private GameEnergyUIPanel _gameEnergyUIPanel; - private GameEnergyCounter _gameEnergyCounter; - private Animator _levelFailedAnimator; - private GameObject _levelFailedGameObject; + public class NoFailGameEnergy : MonoBehaviour + { + private GameEnergyUIPanel _gameEnergyUIPanel; + private GameEnergyCounter _gameEnergyCounter; + private Animator _levelFailedAnimator; + private GameObject _levelFailedGameObject; + public static bool limitLevelFail = false; + public static bool hasFailed; + private bool _isFailedVisible; - private bool _isFailedVisible; + private void Awake() + {/* + hasFailed = false; + limitLevelFail = Config.GetBool("PracticePlugin", "limitLevelFailDisplay", false, true); - private void Awake() - { _gameEnergyUIPanel = Resources.FindObjectsOfTypeAll().FirstOrDefault(); if (_gameEnergyUIPanel == null) return; - _gameEnergyUIPanel.EnableEnergyPanel(true); + _gameEnergyUIPanel.gameObject.SetActive(true); _gameEnergyCounter = Resources.FindObjectsOfTypeAll().FirstOrDefault(); - var levelFailedController = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + _gameEnergyCounter.gameEnergyDidChangeEvent += _gameEnergyUIPanel.HandleGameEnergyDidChange; + var levelFailedController = Resources.FindObjectsOfTypeAll().FirstOrDefault(); if (levelFailedController == null) return; var textEffect = levelFailedController.GetPrivateField("_levelFailedTextEffect"); _levelFailedAnimator = textEffect.GetPrivateField("_animator"); _levelFailedGameObject = GameObject.Find("LevelFailedTextEffect"); - } + */ + } - private void LateUpdate() - { + private void LateUpdate() + {/* if (_isFailedVisible) return; if (_gameEnergyCounter.energy > 1E-05f) return; + if(limitLevelFail == true) + { + if (hasFailed == false) + StartCoroutine(LevelFailedRoutine()); + if (hasFailed == true) + _gameEnergyCounter.AddEnergy(0.5f); + } + else + { StartCoroutine(LevelFailedRoutine()); - } - - private IEnumerator LevelFailedRoutine() - { - _isFailedVisible = true; - - _levelFailedGameObject.SetActive(false); - _levelFailedAnimator.enabled = true; - yield return new WaitForSeconds(0.1f); - _levelFailedGameObject.SetActive(true); - var waitTime = Time.realtimeSinceStartup + 3; - while (Time.realtimeSinceStartup < waitTime) - { - _gameEnergyCounter.AddEnergy(-1); - yield return null; - } - _levelFailedGameObject.SetActive(false); - - _gameEnergyCounter.AddEnergy(0.5f); - _isFailedVisible = false; - } - } + _gameEnergyCounter.AddEnergy(0.5f); + } + */ + } + + private IEnumerator LevelFailedRoutine() + { + + if (!(limitLevelFail == true && hasFailed == true)) + { + _isFailedVisible = true; + + _levelFailedGameObject.SetActive(false); + _levelFailedAnimator.enabled = true; + yield return new WaitForSeconds(0.1f); + _levelFailedGameObject.SetActive(true); + var waitTime = Time.realtimeSinceStartup + 3; + while (Time.realtimeSinceStartup < waitTime) + { + _gameEnergyCounter.ProcessEnergyChange(-1); + yield return null; + } + _levelFailedGameObject.SetActive(false); + + _gameEnergyCounter.ProcessEnergyChange(0.5f); + _isFailedVisible = false; + if (limitLevelFail == true) + hasFailed = true; + } + + } + } } \ No newline at end of file diff --git a/PracticePlugin/Plugin.cs b/PracticePlugin/Plugin.cs index 100e655..1f82bb2 100644 --- a/PracticePlugin/Plugin.cs +++ b/PracticePlugin/Plugin.cs @@ -2,267 +2,459 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using IllusionPlugin; +using HMUI; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using Zenject; using Object = UnityEngine.Object; +//using CustomUI.GameplaySettings; +using IPA; +using BS_Utils.Gameplay; +using BeatSaberMarkupLanguage; +using ModestTree; +using BeatSaberMarkupLanguage.ViewControllers; +using UnityEngine.UIElements; +using BS_Utils.Utilities; namespace PracticePlugin { - public class Plugin : IPlugin - { - public string Name - { - get { return "Practice Plugin"; } - } - - public string Version - { - get { return "v3.0.2"; } - } - - public const float MaxSize = 5.05f; - public const float StepSize = 0.05f; - - public const string MenuSceneName = "Menu"; - public const string GameSceneName = "GameCore"; - public const string ContextSceneName = "StandardLevel"; - - public static GameObject SettingsObject { get; private set; } - - public static float TimeScale - { - get { return _timeScale; } - private set - { - _timeScale = value; - if (!IsEqualToOne(_timeScale)) - { - HasTimeScaleChanged = true; - - if (AudioTimeSync != null) - { - AudioTimeSync.forcedAudioSync = true; - } - } - else - { - if (AudioTimeSync != null) - { - AudioTimeSync.forcedAudioSync = false; - } - } - - if (_songAudio != null) - { - _songAudio.pitch = _timeScale; - } - } - } - - private static float _timeScale = 1; - - public static bool NoFail { get; private set; } - - public static bool HasTimeScaleChanged { get; private set; } - - public static bool PlayingNewSong { get; private set; } - - private static bool _init; - private static MainGameSceneSetupData _mainGameSceneSetupData; - public static AudioTimeSyncController AudioTimeSync { get; private set; } - private static AudioSource _songAudio; - private static string _lastLevelId; - private static UIElementsCreator _uiElementsCreator; - private static bool _resetNoFail; - - public void OnApplicationStart() - { - if (_init) return; - _init = true; - SceneManager.sceneLoaded += SceneManagerOnSceneLoaded; - } - - public void OnApplicationQuit() - { - SceneManager.sceneLoaded -= SceneManagerOnSceneLoaded; - } - - private void SceneManagerOnSceneLoaded(Scene scene, LoadSceneMode mode) - { - if (scene.name == MenuSceneName) - { - if (_resetNoFail) - { - var resultsViewController = - Resources.FindObjectsOfTypeAll().FirstOrDefault(); - if (resultsViewController != null) - resultsViewController.continueButtonPressedEvent += - ResultsViewControllerOnContinueButtonPressedEvent; - } - - if (SettingsObject != null) return; - - var volumeSettings = Resources.FindObjectsOfTypeAll().FirstOrDefault(); - - if (volumeSettings == null) return; - - volumeSettings.gameObject.SetActive(false); - SettingsObject = Object.Instantiate(volumeSettings.gameObject); - SettingsObject.SetActive(false); - volumeSettings.gameObject.SetActive(true); - - if (SettingsObject == null) return; - - var volume = SettingsObject.GetComponent(); - ReflectionUtil.CopyComponent(volume, typeof(IncDecSettingsController), - typeof(SpeedSettingsController), SettingsObject); - Object.DestroyImmediate(volume); - - SettingsObject.GetComponentInChildren().text = "SPEED"; - Object.DontDestroyOnLoad(SettingsObject); - } - else if (scene.name == GameSceneName) - { - CustomEffectPoolsInstaller customEffectPoolsInstaller = null; - var effectPoolsInstaller = Resources.FindObjectsOfTypeAll().FirstOrDefault(); - if (effectPoolsInstaller != null) - { - customEffectPoolsInstaller = (CustomEffectPoolsInstaller) ReflectionUtil.CopyComponent(effectPoolsInstaller, - typeof(EffectPoolsInstaller), typeof(CustomEffectPoolsInstaller), effectPoolsInstaller.gameObject); - } - - SceneContext sceneContext = null; - SceneDecoratorContext sceneDecoratorContext = null; - - foreach (var gameObject in scene.GetRootGameObjects()) - { - if (sceneContext == null) - { - sceneContext = gameObject.GetComponentInChildren(true); - } - } - - foreach (var gameObject in SceneManager.GetSceneByName(ContextSceneName).GetRootGameObjects()) - { - if (sceneDecoratorContext == null) - { - sceneDecoratorContext = gameObject.GetComponentInChildren(true); - } - } - - - if (sceneContext != null && sceneDecoratorContext != null) - { - var prop = typeof(Context).GetField("_installers", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - var installersList = (List) prop.GetValue(sceneDecoratorContext); - installersList.Remove(effectPoolsInstaller); - Object.DestroyImmediate(effectPoolsInstaller); - installersList.Add(customEffectPoolsInstaller); - } - - if (_mainGameSceneSetupData == null) - { - _mainGameSceneSetupData = Resources.FindObjectsOfTypeAll().FirstOrDefault(); - if (_mainGameSceneSetupData == null) return; - _mainGameSceneSetupData.didFinishEvent += MainGameSceneSetupDataOnDidFinishEvent; - } - - if (_lastLevelId != _mainGameSceneSetupData.difficultyLevel.level.levelID && - !string.IsNullOrEmpty(_lastLevelId)) - { - PlayingNewSong = true; - HasTimeScaleChanged = false; - TimeScale = 1; - _lastLevelId = _mainGameSceneSetupData.difficultyLevel.level.levelID; - } - else - { - PlayingNewSong = false; - } - - if (IsEqualToOne(TimeScale)) - { - HasTimeScaleChanged = false; - } - - _lastLevelId = _mainGameSceneSetupData.difficultyLevel.level.levelID; - - AudioTimeSync = Resources.FindObjectsOfTypeAll().FirstOrDefault(); - _songAudio = AudioTimeSync.GetPrivateField("_audioSource"); - NoFail = !_mainGameSceneSetupData.gameplayOptions.validForScoreUse; - - if (!NoFail) - { - TimeScale = Mathf.Clamp(TimeScale, 1, MaxSize); - } - - var canvas = Resources.FindObjectsOfTypeAll() - .FirstOrDefault(x => x.name == "Buttons") - ?.transform.parent; - - if (canvas == null) return; - - _uiElementsCreator = canvas.gameObject.AddComponent(); - _uiElementsCreator.ValueChangedEvent += UIElementsCreatorOnValueChangedEvent; - _uiElementsCreator.Init(); - TimeScale = TimeScale; - } - } - - private void ResultsViewControllerOnContinueButtonPressedEvent(ResultsViewController obj) - { - PersistentSingleton.instance.gameDynamicData.GetCurrentPlayerDynamicData() - .gameplayOptions.noEnergy = false; - } - - private void MainGameSceneSetupDataOnDidFinishEvent(MainGameSceneSetupData arg1, LevelCompletionResults results) - { - if (!NoFail && HasTimeScaleChanged && results != null && - results.levelEndStateType == LevelCompletionResults.LevelEndStateType.Cleared) - { - arg1.gameplayOptions.noEnergy = true; - _resetNoFail = true; - } - } - - private void UIElementsCreatorOnValueChangedEvent(float timeScale) - { - if (!IsEqualToOne(timeScale)) - { - HasTimeScaleChanged = true; - } - - TimeScale = timeScale; - } - - private static bool IsEqualToOne(float value) - { - return Math.Abs(value - 1) < 0.000000001f; - } - - public void OnLevelWasLoaded(int level) - { - - } - - public void OnLevelWasInitialized(int level) - { - - } - - public void OnUpdate() - { - if (_uiElementsCreator == null || _uiElementsCreator.SongSeeker == null) return; - _uiElementsCreator.SongSeeker.OnUpdate(); - } - - public void OnFixedUpdate() - { - - } - } -} \ No newline at end of file + [Plugin(RuntimeOptions.SingleStartInit)] + public class Plugin + { + + public const float SpeedMaxSize = 5.05f; + public const float SpeedStepSize = 0.05f; + + public const int NjsMaxSize = 100; + public const int NjstepSize = 1; + + public const string MenuSceneName = "MenuViewControllers"; + public const string GameSceneName = "GameCore"; + public const string ContextSceneName = "GameplayCore"; + + public string failTime { get; private set; } + internal bool showFailTextNext { get; set; } + // public static GameObject SpeedSettingsObject { get; private set; } + // public static GameObject NjsSettingsObject { get; private set; } + // public static GameObject SpawnOffsetSettingsObject { get; private set; } + internal static bool startWithFullEnergy = false; + internal static bool disablePitchCorrection = false; + internal static bool adjustNJSWithSpeed = false; + internal static float TimeScale + { + get { return _timeScale; } + set + { + _timeScale = value; + if (!AudioTimeSync) return; + if (!_spawnController) return; + AudioTimeSyncController.InitData initData = AudioTimeSync.GetPrivateField("_initData"); + AudioTimeSyncController.InitData newInitData = new AudioTimeSyncController.InitData(initData.audioClip, + AudioTimeSync.songTime, initData.songTimeOffset, _timeScale); + AudioTimeSync.SetPrivateField("_initData", newInitData); + //Chipmunk Removal as per base game + if (!disablePitchCorrection) + { + if (_timeScale == 1f) + _mixer.musicPitch = 1; + else + _mixer.musicPitch = 1f / _timeScale; + } + else + { + _mixer.musicPitch = 1f; + } + ResetTimeSync(AudioTimeSync, _timeScale, newInitData); + // AudioTimeSync.StartSong(); + + + return; + // AudioTimeSync.SetPrivateField("_timeScale", value); + // AudioTimeSync.Init(_songAudio.clip, _songAudio.time, AudioTimeSync.GetPrivateField("_songTimeOffset"), value); + if (_timeScale == 1f) + _mixer.musicPitch = 1; + else + _mixer.musicPitch = 1f / _timeScale; + if (!IsEqualToOne(_timeScale)) + { + + if (AudioTimeSync != null) + { + // AudioTimeSync.forcedNoAudioSync = true; + } + } + else + { + if (AudioTimeSync != null) + { + // AudioTimeSync.forcedNoAudioSync = false; + } + } + if (AudioTimeSync != null) + { + // AudioTimeSync.SetPrivateField("_timeScale", _timeScale); // = _timeScale; + // AudioTimeSync.Init(_songAudio.clip, _songAudio.time, + // AudioTimeSync.GetPrivateField("_songTimeOffset") - AudioTimeSync.GetPrivateField("_audioLatency").value, _timeScale); + Console.WriteLine("Called TimeScale"); + + if (_songAudio != null) + { + _songAudio.pitch = _timeScale; + } + // AudioTimeSync.forcedNoAudioSync = true; + // float num = AudioTimeSync.GetPrivateField("_startSongTime") + AudioTimeSync.GetPrivateField("_songTimeOffset"); + // AudioTimeSync.SetPrivateField("_audioStartTimeOffsetSinceStart", (Time.timeSinceLevelLoad * _timeScale) - num); + // AudioTimeSync.SetPrivateField("_fixingAudioSyncError", false); + // AudioTimeSync.SetPrivateField("_prevAudioSamplePos", _songAudio.timeSamples); + // AudioTimeSync.SetPrivateField("_playbackLoopIndex", 0); + // AudioTimeSync.SetPrivateField("_dspTimeOffset", AudioSettings.dspTime - (double)num); + // AudioTimeSync.SetPrivateField("_timeScale", _timeScale); // = _timeScale; + } + + + } + } + public static void ResetTimeSync(AudioTimeSyncController timeSync, float newTimeScale, AudioTimeSyncController.InitData newData) + { + + timeSync.SetPrivateField("_timeScale", newTimeScale); + timeSync.SetPrivateField("_startSongTime", timeSync.songTime); + timeSync.SetPrivateField("_audioStartTimeOffsetSinceStart", + timeSync.GetProperty("timeSinceStart") - (timeSync.songTime + newData.songTimeOffset)); + timeSync.SetPrivateField("_fixingAudioSyncError", false); + timeSync.SetPrivateField("_playbackLoopIndex", 0); + timeSync.audioSource.pitch = newTimeScale; + } + + private static float _timeScale = 1; + + public static bool PracticeMode { get; private set; } + + public static bool PlayingNewSong { get; private set; } + + private static bool _init; + public static BS_Utils.Gameplay.LevelData _levelData { get; private set; } + public static BS_Utils.Utilities.Config Config = new BS_Utils.Utilities.Config("PracticePlugin"); + public static BeatmapObjectSpawnController _spawnController { get; private set; } + public static AudioTimeSyncController AudioTimeSync { get; private set; } + private static AudioManagerSO _mixer; + private static AudioSource _songAudio; + private static string _lastLevelId; + public static UIElementsCreator _uiElementsCreator; + private static ResultsViewController resultsViewController; + private static bool _resetNoFail; + private static bool showTimeFailed = true; + private static TextMeshProUGUI failText; + [OnStart] + public void OnApplicationStart() + { + if (_init) return; + _init = true; + + startWithFullEnergy = Config.GetBool("PracticePlugin", "startWithFullEnergy", false, true); + showTimeFailed = Config.GetBool("PracticePlugin", "Show Time Failed", true, true); + disablePitchCorrection = Config.GetBool("PracticePlugin", "Disable Pitch Correction", false, true); + adjustNJSWithSpeed = Config.GetBool("PracticePlugin", "Adjust NJS With Speed", false, true); + SceneManager.activeSceneChanged += OnActiveSceneChanged; + SceneManager.sceneLoaded += OnSceneLoaded; + + } + + public void OnSceneLoaded(Scene arg0, LoadSceneMode arg1) + { + + } + + public void OnApplicationQuit() + { + + } + + public void OnActiveSceneChanged(Scene oldScene, Scene newScene) + { + Object.Destroy(Resources.FindObjectsOfTypeAll().FirstOrDefault()?.gameObject); + if (newScene.name == MenuSceneName) + { + resultsViewController = + Resources.FindObjectsOfTypeAll().FirstOrDefault(); + if (resultsViewController != null) + { + + resultsViewController.didActivateEvent -= ResultsViewController_didActivateEvent; + resultsViewController.didActivateEvent += ResultsViewController_didActivateEvent; + } + + } + else if (newScene.name == GameSceneName) + { + + _levelData = BS_Utils.Plugin.LevelData; + if (_levelData.IsSet == false) return; + BS_Utils.Plugin.LevelDidFinishEvent -= MainGameSceneSetupDataOnDidFinishEvent; + BS_Utils.Plugin.LevelDidFinishEvent += MainGameSceneSetupDataOnDidFinishEvent; + + + if (_lastLevelId != _levelData.GameplayCoreSceneSetupData.difficultyBeatmap.level.levelID && + !string.IsNullOrEmpty(_lastLevelId)) + { + PlayingNewSong = true; + // TimeScale = 1; + _lastLevelId = _levelData.GameplayCoreSceneSetupData.difficultyBeatmap.level.levelID; + } + else + { + PlayingNewSong = false; + } + + + _lastLevelId = _levelData.GameplayCoreSceneSetupData.difficultyBeatmap.level.levelID; + _mixer = Resources.FindObjectsOfTypeAll().LastOrDefault(); + AudioTimeSync = Resources.FindObjectsOfTypeAll().LastOrDefault(); + _songAudio = AudioTimeSync.GetPrivateField("_audioSource"); + + PracticeMode = (_levelData.Mode == BS_Utils.Gameplay.Mode.Standard && _levelData.GameplayCoreSceneSetupData.practiceSettings != null && !BS_Utils.Gameplay.Gamemode.IsIsolatedLevel); + + + if (!PracticeMode) + { + _timeScale = Mathf.Clamp(TimeScale, 1, SpeedMaxSize); + } + if (PracticeMode) + { + if (_levelData.GameplayCoreSceneSetupData.practiceSettings.songSpeedMul != 1f) + _timeScale = _levelData.GameplayCoreSceneSetupData.practiceSettings.songSpeedMul; + else + _timeScale = _levelData.GameplayCoreSceneSetupData.gameplayModifiers.songSpeedMul; + SharedCoroutineStarter.instance.StartCoroutine(DelayedSetup()); + } + + } + } + + + private void ResultsViewController_didActivateEvent(bool firstActivation, bool addedToHierarchy, bool screenSystemEnabling) + { + if (showFailTextNext && showTimeFailed) + { + if (failText == null) + failText = BeatSaberMarkupLanguage.BeatSaberUI.CreateText(resultsViewController.rectTransform, failTime, new Vector2(15f, -35f)); + else + failText.text = failTime; + failText.richText = true; + } + else + { + if (failText != null) + failText.text = ""; + } + showFailTextNext = false; + } + + public System.Collections.IEnumerator DelayedSetup() + { + yield return new WaitForSeconds(0.1f); + try + { + if (_spawnController == null) + { + _spawnController = Resources.FindObjectsOfTypeAll().LastOrDefault(); + + } + Console.WriteLine("Atemmpting Practice Plugin UI"); + + var canvas = GameObject.Find("PauseMenu").transform.Find("Wrapper").transform.Find("MenuWrapper").transform.Find("Canvas"); + + if (canvas == null) + { + Console.WriteLine("Canvas Null"); + } + + + GameObject uiObj = new GameObject("PracticePlugin Seeker UI", typeof(RectTransform)); + + (uiObj.transform as RectTransform).anchorMin = new Vector2(0, 0); + (uiObj.transform as RectTransform).anchorMax = new Vector2(1, 1); + (uiObj.transform as RectTransform).sizeDelta = new Vector2(0, 0); + + _uiElementsCreator = uiObj.AddComponent(); + var practiceUI = new GameObject("PracticePlugin Adjustment UI").AddComponent(); + UIElementsCreator.practiceUI = practiceUI; + BSMLParser.instance.Parse(BeatSaberMarkupLanguage.Utilities.GetResourceContent(Assembly.GetExecutingAssembly(), "PracticePlugin.PracticeUI.bsml"), canvas.gameObject, practiceUI); + _uiElementsCreator.Init(); + + uiObj.transform.SetParent(canvas, false); + + uiObj.transform.localScale = new Vector3(1, 1, 1); + uiObj.transform.localPosition = new Vector3(0f, -3f, 0f); + + new GameObject("Practice Plugin Behavior").AddComponent(); + if (startWithFullEnergy) + { + GameEnergyCounter energyCounter = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + if (energyCounter != null) + energyCounter.ProcessEnergyChange(1 - energyCounter.energy); + } + + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + } + + + private void MainGameSceneSetupDataOnDidFinishEvent(StandardLevelScenesTransitionSetupDataSO levelData, LevelCompletionResults results) + { + if (results.levelEndStateType == LevelCompletionResults.LevelEndStateType.Failed) + { + float endTime = results.endSongTime; + float length = _songAudio.clip.length; + failTime = $"<#ff0000>Failed At - {Math.Floor(endTime / 60):N0}:{Math.Floor(endTime % 60):00} / {Math.Floor(length / 60):N0}:{Math.Floor(length % 60):00}"; + showFailTextNext = true; + + } + + } + + + private static bool IsEqualToOne(float value) + { + return Math.Abs(value - 1) < 0.000000001f; + } + + public void OnLevelWasLoaded(int level) + { + + } + + public void OnLevelWasInitialized(int level) + { + + } + + + public void OnFixedUpdate() + { + + } + + public static void AdjustNJS(float njs) + { + + float halfJumpDur = 4f; + float maxHalfJump = _spawnController.GetPrivateField("_maxHalfJumpDistance"); + float noteJumpStartBeatOffset = _levelData.GameplayCoreSceneSetupData.difficultyBeatmap.noteJumpStartBeatOffset; + float moveSpeed = _spawnController.GetPrivateField("_moveSpeed"); + float moveDir = _spawnController.GetPrivateField("_moveDurationInBeats"); + float jumpDis; + float spawnAheadTime; + float moveDis; + float bpm = _spawnController.GetPrivateField("_beatsPerMinute"); + float num = 60f / bpm; + moveDis = moveSpeed * num * moveDir; + while (njs * num * halfJumpDur > maxHalfJump) + { + halfJumpDur /= 2f; + } + halfJumpDur += noteJumpStartBeatOffset; + if (halfJumpDur < 1f) halfJumpDur = 1f; + // halfJumpDur = spawnController.GetPrivateField("_halfJumpDurationInBeats"); + jumpDis = njs * num * halfJumpDur * 2f; + spawnAheadTime = moveDis / moveSpeed + jumpDis * 0.5f / njs; + _spawnController.SetPrivateField("_halfJumpDurationInBeats", halfJumpDur); + _spawnController.SetPrivateField("_spawnAheadTime", spawnAheadTime); + _spawnController.SetPrivateField("_jumpDistance", jumpDis); + _spawnController.SetPrivateField("_noteJumpMovementSpeed", njs); + _spawnController.SetPrivateField("_moveDistance", moveDis); + + + } + public static void AdjustSpawnOffset(float offset) + { + float njs = _spawnController.GetPrivateField("_initData").noteJumpMovementSpeed; + float halfJumpDur = 4f; + float maxHalfJump = _spawnController.GetPrivateField("_maxHalfJumpDistance"); + float noteJumpStartBeatOffset = offset; + float moveSpeed = _spawnController.GetPrivateField("_moveSpeed"); + float moveDir = _spawnController.GetPrivateField("_moveDurationInBeats"); + float jumpDis; + float spawnAheadTime; + float moveDis; + float bpm = _spawnController.GetPrivateField("_beatsPerMinute"); + float num = 60f / bpm; + moveDis = moveSpeed * num * moveDir; + while (njs * num * halfJumpDur > maxHalfJump) + { + halfJumpDur /= 2f; + } + halfJumpDur += noteJumpStartBeatOffset; + if (halfJumpDur < 1f) halfJumpDur = 1f; + // halfJumpDur = spawnController.GetPrivateField("_halfJumpDurationInBeats"); + jumpDis = njs * num * halfJumpDur * 2f; + spawnAheadTime = moveDis / moveSpeed + jumpDis * 0.5f / njs; + _spawnController.SetPrivateField("_halfJumpDurationInBeats", halfJumpDur); + _spawnController.SetPrivateField("_spawnAheadTime", spawnAheadTime); + _spawnController.SetPrivateField("_jumpDistance", jumpDis); + _spawnController.SetPrivateField("_noteJumpMovementSpeed", njs); + _spawnController.SetPrivateField("_moveDistance", moveDis); + + + } + + public static void UpdateSpawnMovementData(float njs, float noteJumpStartBeatOffset) + { + BeatmapObjectSpawnMovementData spawnMovementData = + _spawnController.GetPrivateField("_beatmapObjectSpawnMovementData"); + + float bpm = _spawnController.GetPrivateField("_variableBpmProcessor").currentBpm; + + + if (adjustNJSWithSpeed) + { + float newNJS = njs * (1 / TimeScale); + njs = newNJS; + } + + + + spawnMovementData.SetPrivateField("_startNoteJumpMovementSpeed", njs); + spawnMovementData.SetPrivateField("_noteJumpStartBeatOffset", noteJumpStartBeatOffset); + + spawnMovementData.Update(bpm, + _spawnController.GetPrivateField("_jumpOffsetY")); + + + } + public void OnSceneUnloaded(Scene scene) + { + } + + public static TextMeshProUGUI CreateText(RectTransform parent, string text, Vector2 anchoredPosition) + { + return CreateText(parent, text, anchoredPosition, new Vector2(60f, 10f)); + } + + public static TextMeshProUGUI CreateText(RectTransform parent, string text, Vector2 anchoredPosition, Vector2 sizeDelta) + { + GameObject gameObj = new GameObject("CustomUIText"); + gameObj.SetActive(false); + + TextMeshProUGUI textMesh = gameObj.AddComponent(); + textMesh.font = UnityEngine.Object.Instantiate(Resources.FindObjectsOfTypeAll().First(t => t.name == "Teko-Medium SDF No Glow")); + textMesh.rectTransform.SetParent(parent, false); + textMesh.text = text; + textMesh.fontSize = 4; + textMesh.color = Color.white; + + textMesh.rectTransform.anchorMin = new Vector2(0.5f, 0.5f); + textMesh.rectTransform.anchorMax = new Vector2(0.5f, 0.5f); + textMesh.rectTransform.sizeDelta = sizeDelta; + textMesh.rectTransform.anchoredPosition = anchoredPosition; + + gameObj.SetActive(true); + return textMesh; + } + } +} diff --git a/PracticePlugin/PoolExtensions.cs b/PracticePlugin/PoolExtensions.cs index ce7d6a3..1e2e700 100644 --- a/PracticePlugin/PoolExtensions.cs +++ b/PracticePlugin/PoolExtensions.cs @@ -2,16 +2,17 @@ using UnityEngine; namespace PracticePlugin -{ - public static class PoolExtensions - { - public static void DespawnAll (this MemoryPoolWithActiveItems memoryPool) where T : Component - { - var activeItems = memoryPool.activeItems.ToList(); - foreach (var activeItem in activeItems) - { - memoryPool.Despawn(activeItem); - } - } - } +{/* + public static class PoolExtensions + { + public static void DespawnAll(this MemoryPoolWithActiveItems memoryPool) where T : Component + { + var activeItems = memoryPool.activeItems.ToList(); + foreach (var activeItem in activeItems) + { + memoryPool.Despawn(activeItem); + } + } + } + */ } \ No newline at end of file diff --git a/PracticePlugin/PracticePlugin.csproj b/PracticePlugin/PracticePlugin.csproj index 15b2b7e..c54ed37 100644 --- a/PracticePlugin/PracticePlugin.csproj +++ b/PracticePlugin/PracticePlugin.csproj @@ -9,8 +9,9 @@ Properties PracticePlugin PracticePlugin - v4.6 + v4.7.2 512 + AnyCPU @@ -32,63 +33,107 @@ 4 - - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Assembly-CSharp.dll + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\BeatmapCore.dll - - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\IllusionPlugin.dll + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Plugins\BSML.dll + + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Plugins\BS_Utils.dll + + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Colors.dll + + + False + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Core.dll + + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\GameplayCore.dll + + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\HMLib.dll + + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\HMLibAttributes.dll + + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\HMUI.dll + + + False + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\IPA.Loader.dll + + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Main.dll + + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\MediaLoader.dll + + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Polyglot.dll - - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\TextMeshPro-1.0.55.2017.1.0b12.dll + + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Unity.TextMeshPro.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.dll + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.AnimationModule.dll + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.AnimationModule.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.AudioModule.dll + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.AudioModule.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.UI.dll + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.UI.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.UIElementsModule.dll + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.UIElementsModule.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.UIModule.dll + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.UIModule.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Zenject.dll + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Zenject.dll - ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Zenject-usage.dll + C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\Zenject-usage.dll + - + - + + + + + + + + xcopy "$(ProjectDir)\bin\Debug\PracticePlugin.dll" "C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\Plugins" /Y + \ No newline at end of file diff --git a/PracticePlugin/PracticeUI.bsml b/PracticePlugin/PracticeUI.bsml new file mode 100644 index 0000000..53170fa --- /dev/null +++ b/PracticePlugin/PracticeUI.bsml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/PracticePlugin/PracticeUI.cs b/PracticePlugin/PracticeUI.cs new file mode 100644 index 0000000..db4f829 --- /dev/null +++ b/PracticePlugin/PracticeUI.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BeatSaberMarkupLanguage; +using BeatSaberMarkupLanguage.Components; +using BeatSaberMarkupLanguage.Attributes; +using HMUI; +using UnityEngine; + +namespace PracticePlugin +{ + public class PracticeUI : MonoBehaviour + { + + private float _speed = BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData.practiceSettings.songSpeedMul; + [UIValue("speed")] + public float speed + { + get => _speed; + set + { + _speed = value; + // Plugin.TimeScale = PracticeUI.instance.speed; + } + } + [UIAction("setSpeed")] + void SetSpeed(float value) + { + speed = value; + } + private float _njs = BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData.difficultyBeatmap.noteJumpMovementSpeed != 0? + BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData.difficultyBeatmap.noteJumpMovementSpeed : + BeatmapDifficultyMethods.NoteJumpMovementSpeed(BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData.difficultyBeatmap.difficulty); + + [UIValue("njs")] + public float njs + { + get => _njs; + set + { + _njs = value; + } + } + [UIAction("setnjs")] + void SetNjs(float value) + { + njs = value; + UIElementsCreator.NjsController_ValueChangedEvent(value); + } + private float _offset = BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData.difficultyBeatmap.noteJumpStartBeatOffset; + [UIValue("offset")] + public float offset + { + get => _offset; + set + { + _offset = value; + } + } + [UIAction("setoffset")] + void SetSpawnOffset(float value) + { + offset = value; + UIElementsCreator.SpawnOffsetController_ValueChangedEvent(value); + } + + [UIAction("speedFormatter")] + string speedForValue(float value) + { + return $"{(int)(value * 100)}%"; + } + [UIAction("njsFormatter")] + string njsForValue(float value) + { + return value == UIElementsCreator.defaultNJS ? $"{value}" : $"{value}"; + } + [UIAction("spawnOffsetFormatter")] + string offsetForValue(float value) + { + return value == UIElementsCreator.defaultOffset ? $"{value.ToString("F2")}" : $"{value.ToString("F2")}"; + } + + [UIAction("#post-parse")] + void PostParse() + { + if(gameObject.GetComponent() == null) + gameObject.AddComponent(); + } + } +} diff --git a/PracticePlugin/Properties/AssemblyInfo.cs b/PracticePlugin/Properties/AssemblyInfo.cs index 925953f..1e3ec17 100644 --- a/PracticePlugin/Properties/AssemblyInfo.cs +++ b/PracticePlugin/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.0.2.0")] -[assembly: AssemblyFileVersion("3.0.2.0")] \ No newline at end of file +[assembly: AssemblyVersion("4.12.0.0")] +[assembly: AssemblyFileVersion("4.12.0.0")] \ No newline at end of file diff --git a/PracticePlugin/SongSeekBeatmapHandler.cs b/PracticePlugin/SongSeekBeatmapHandler.cs index fcb194c..8d07879 100644 --- a/PracticePlugin/SongSeekBeatmapHandler.cs +++ b/PracticePlugin/SongSeekBeatmapHandler.cs @@ -1,140 +1,176 @@ -using System; +using BS_Utils.Utilities; +using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace PracticePlugin { - public static class SongSeekBeatmapHandler - { - private static List CallbackList - { - get - { - if (_beatmapObjectCallbackController == null || _callbackList == null) - { - _beatmapObjectCallbackController = Resources.FindObjectsOfTypeAll() - .FirstOrDefault(); - - if (_beatmapObjectCallbackController != null) - { - _callbackList = - _beatmapObjectCallbackController - .GetPrivateField>( - "_beatmapObjectCallbackData"); - - _beatmapData = _beatmapObjectCallbackController - .GetPrivateField("_beatmapDataModel").beatmapData; - } - - if (_beatmapObjectSpawnController == null) - { - _beatmapObjectSpawnController = Resources.FindObjectsOfTypeAll() - .FirstOrDefault(); - if (_beatmapObjectSpawnController != null) - { - _noteAPool = _beatmapObjectSpawnController.GetPrivateField("_noteAPool"); - _noteBPool = _beatmapObjectSpawnController.GetPrivateField("_noteBPool"); - _bombNotePool = _beatmapObjectSpawnController.GetPrivateField("_bombNotePool"); - _fullHeightObstaclePool = - _beatmapObjectSpawnController.GetPrivateField("_fullHeightObstaclePool"); - _topObstaclePool = _beatmapObjectSpawnController.GetPrivateField("_topObstaclePool"); - } - } - - if (_noteCutSoundEffectManager == null) - { - _noteCutSoundEffectManager = Resources.FindObjectsOfTypeAll() - .FirstOrDefault(); - } - } - - return _callbackList; - } - } - - private static List _callbackList; - private static BeatmapObjectCallbackController _beatmapObjectCallbackController; - private static BeatmapObjectSpawnController _beatmapObjectSpawnController; - private static NoteCutSoundEffectManager _noteCutSoundEffectManager; - - private static NoteController.Pool _noteAPool; - private static NoteController.Pool _noteBPool; - private static NoteController.Pool _bombNotePool; - private static ObstacleController.Pool _fullHeightObstaclePool; - private static ObstacleController.Pool _topObstaclePool; - - private static BeatmapData _beatmapData; - - public static void OnSongTimeChanged(float newSongTime, float aheadTime) - { - foreach (var callbackData in CallbackList) - { - for (var i = 0; i < _beatmapData.beatmapLinesData.Length; i++) - { - callbackData.nextObjectIndexInLine[i] = 0; - while (callbackData.nextObjectIndexInLine[i] < _beatmapData.beatmapLinesData[i].beatmapObjectsData.Length) - { - var beatmapObjectData = _beatmapData.beatmapLinesData[i].beatmapObjectsData[callbackData.nextObjectIndexInLine[i]]; - if (beatmapObjectData.time - aheadTime >= newSongTime) - { - break; - } - - callbackData.nextObjectIndexInLine[i]++; - } - } - } - - var newNextEventIndex = 0; - - while (newNextEventIndex < _beatmapData.beatmapEventData.Length) - { - var beatmapEventData = _beatmapData.beatmapEventData[newNextEventIndex]; - if (beatmapEventData.time >= newSongTime) - { - break; - } - - newNextEventIndex++; - } - - _beatmapObjectCallbackController.SetPrivateField("_nextEventIndex", newNextEventIndex); - - var notesA = _noteAPool.activeItems.ToList(); - foreach (var noteA in notesA) - { - _beatmapObjectSpawnController.Despawn(noteA); - } - - var notesB = _noteBPool.activeItems.ToList(); - foreach (var noteB in notesB) - { - _beatmapObjectSpawnController.Despawn(noteB); - } - - var bombs = _bombNotePool.activeItems.ToList(); - foreach (var bomb in bombs) - { - _beatmapObjectSpawnController.Despawn(bomb); - } - - var fullHeights = _fullHeightObstaclePool.activeItems.ToList(); - foreach (var fullHeight in fullHeights) - { - _beatmapObjectSpawnController.Despawn(fullHeight); - } - - var tops = _topObstaclePool.activeItems.ToList(); - foreach (var top in tops) - { - _beatmapObjectSpawnController.Despawn(top); - } - - Plugin.AudioTimeSync.SetPrivateField("_prevAudioSamplePos", -1); - Plugin.AudioTimeSync.GetPrivateField("_songTime").value = newSongTime; - _noteCutSoundEffectManager.SetPrivateField("_prevNoteATime", -1); - _noteCutSoundEffectManager.SetPrivateField("_prevNoteBTime", -1); - } - } + public static class SongSeekBeatmapHandler + { + private static List CallbackList + { + get + { + if (_beatmapObjectCallbackController == null || _callbackList == null) + { + _beatmapObjectCallbackController = Resources.FindObjectsOfTypeAll() + .LastOrDefault(); + + if (_beatmapObjectCallbackController != null) + { + _callbackList = + _beatmapObjectCallbackController + .GetPrivateField>( + "_beatmapObjectCallbackData"); + + _beatmapData = _beatmapObjectCallbackController + .GetPrivateField("_beatmapData"); + } + + if (_beatmapObjectSpawnController == null) + { + _beatmapObjectSpawnController = Resources.FindObjectsOfTypeAll() + .LastOrDefault(); + + } + + if (_beatmapObjectManager == null) + { + _beatmapObjectManager = Resources.FindObjectsOfTypeAll().LastOrDefault().GetPrivateField("_beatmapObjectManager") as BasicBeatmapObjectManager; + + if (_beatmapObjectManager != null) + { + _notePool = _beatmapObjectManager.GetPrivateField>("_gameNotePoolContainer"); + _bombNotePool = _beatmapObjectManager.GetPrivateField>("_bombNotePoolContainer"); + _obstaclePool = + _beatmapObjectManager.GetPrivateField>("_obstaclePoolContainer"); + } + } + if (_noteCutSoundEffectManager == null) + { + _noteCutSoundEffectManager = Resources.FindObjectsOfTypeAll() + .LastOrDefault(); + } + } + + return _callbackList; + } + } + + private static List _callbackList; + private static BeatmapObjectCallbackController _beatmapObjectCallbackController; + private static BeatmapObjectSpawnController _beatmapObjectSpawnController; + private static BasicBeatmapObjectManager _beatmapObjectManager; + private static NoteCutSoundEffectManager _noteCutSoundEffectManager; + + private static MemoryPoolContainer _notePool; + private static MemoryPoolContainer _bombNotePool; + private static MemoryPoolContainer _obstaclePool; + + private static BeatmapData _beatmapData; + + public static void OnSongTimeChanged(float newSongTime, float aheadTime) + { + if (_beatmapObjectCallbackController) + _beatmapData = _beatmapObjectCallbackController.GetPrivateField("_beatmapData"); + foreach (var callbackData in CallbackList) + { + for (var i = 0; i < _beatmapData.beatmapLinesData.Count; i++) + { + callbackData.nextObjectIndexInLine[i] = 0; + while (callbackData.nextObjectIndexInLine[i] < _beatmapData.beatmapLinesData[i].beatmapObjectsData.Count) + { + var beatmapObjectData = _beatmapData.beatmapLinesData[i].beatmapObjectsData[callbackData.nextObjectIndexInLine[i]]; + if (beatmapObjectData.time - aheadTime >= newSongTime) + { + break; + } + + callbackData.nextObjectIndexInLine[i]++; + } + } + } + + var newNextEventIndex = 0; + + while (newNextEventIndex < _beatmapData.beatmapEventsData.Count) + { + var beatmapEventData = _beatmapData.beatmapEventsData[newNextEventIndex]; + if (beatmapEventData.time >= newSongTime) + { + break; + } + + newNextEventIndex++; + } + + _beatmapObjectCallbackController.SetPrivateField("_nextEventIndex", newNextEventIndex); + // _beatmapObjectManager.DissolveAllObjects(); + var notes = _beatmapObjectManager.GetField>("_gameNotePoolContainer"); + var bombs = _beatmapObjectManager.GetField>("_bombNotePoolContainer"); + var walls = _beatmapObjectManager.GetField>("_obstaclePoolContainer"); + foreach (var note in notes.activeItems) + { + if (note == null) continue; + note.hide = false; + note.pause = false; + note.enabled = true; + note.gameObject.SetActive(true); + note.Dissolve(0f); + // _beatmapObjectManager.InvokeMethod("Despawn", note as NoteController); + } + foreach (var bomb in bombs.activeItems) + { + if (bomb == null) continue; + bomb.hide = false; + bomb.pause = false; + bomb.enabled = true; + bomb.gameObject.SetActive(true); + bomb.Dissolve(0f); + // _beatmapObjectManager.InvokeMethod("Despawn", bomb as NoteController); + } + foreach (var wall in walls.activeItems) + { + if (wall == null) continue; + wall.hide = false; + wall.pause = false; + wall.enabled = true; + wall.gameObject.SetActive(true); + wall.Dissolve(0f); + //_beatmapObjectManager.InvokeMethod("Despawn", wall); + } + /* + var notesA = _notePool.activeItems.ToList(); + foreach (var noteA in notesA) + { + // Console.WriteLine("Despawning, Length: " + notesA.Count); + _beatmapObjectManager.DissolveAllObjects(noteA); + } + + var notesB = _noteBPool.activeItems.ToList(); + foreach (var noteB in notesB) + { + _beatmapObjectManager.Despawn(noteB); + } + + var bombs = _bombNotePool.activeItems.ToList(); + foreach (var bomb in bombs) + { + _beatmapObjectManager.Despawn(bomb); + } + + var obstacles = _obstaclePool.activeItems.ToList(); + foreach (var obstacle in obstacles) + { + _beatmapObjectManager.Despawn(obstacle); + } + */ + + Plugin.AudioTimeSync.SetPrivateField("_prevAudioSamplePos", -1); + Plugin.AudioTimeSync.SetPrivateField("_songTime", newSongTime); + _noteCutSoundEffectManager.SetPrivateField("_prevNoteATime", -1); + _noteCutSoundEffectManager.SetPrivateField("_prevNoteBTime", -1); + } + } } \ No newline at end of file diff --git a/PracticePlugin/SongSeeker.cs b/PracticePlugin/SongSeeker.cs index 0bfa93a..f31c9c9 100644 --- a/PracticePlugin/SongSeeker.cs +++ b/PracticePlugin/SongSeeker.cs @@ -4,219 +4,238 @@ using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; - +using HMUI; +using BeatSaberMarkupLanguage; +using IPA.Logging; +using BS_Utils.Utilities; +using System.Linq; + namespace PracticePlugin { - public class SongSeeker : MonoBehaviour, IDragHandler, IPointerDownHandler - { - public float PlaybackPosition { get; private set; } - - [SerializeField] private AudioSource _songAudioSource; - private LooperUI _looperUI; - - private Image _seekBackg; - private Image _seekBar; - private Image _seekCursor; - private TMP_Text _currentTime; - private TMP_Text _timeLength; - - private Camera _mainCamera; - - private const float AheadTime = 1f; - - public static readonly Vector2 SeekBarSize = new Vector2(100, 2); - public static readonly float HalfSeekBarSize = SeekBarSize.x / 2; - - private static readonly Vector2 ParentSize = new Vector2(100, 4); - private static readonly Color BackgroundColor = new Color(0, 0, 0, 0.25f); - private static readonly Color ForegroundColor = new Color(0.8f, 0.8f, 0.8f, 0.5f); - - private static readonly Vector2 SeekCursorSize = new Vector2(4, 4); - private static readonly Color SeekCursorColor = new Color(1, 1, 1, 0.5f); - - private static readonly Vector2 TimeTextSize = new Vector2(16, 8); - private const float TimeTextMargin = 4; - - private const float StickToLooperCursorDistance = 0.02f; - private const float LooperUITopMargin = -5f; - - private int _startTimeSamples; - - public void Init() - { - _songAudioSource = Plugin.AudioTimeSync.GetPrivateField("_audioSource"); - var rectTransform = transform as RectTransform; - rectTransform.anchorMin = Vector2.right * 0.5f; - rectTransform.anchorMax = Vector2.right * 0.5f; - rectTransform.sizeDelta = ParentSize; - rectTransform.anchoredPosition = new Vector2(0, 16); - - _seekBackg = new GameObject("Background").AddComponent(); - rectTransform = _seekBackg.rectTransform; - rectTransform.SetParent(transform, false); - rectTransform.sizeDelta = SeekBarSize; - _seekBackg.color = BackgroundColor; - - _seekBar = new GameObject("Seek Bar").AddComponent(); - rectTransform = _seekBar.rectTransform; - rectTransform.SetParent(transform, false); - rectTransform.sizeDelta = SeekBarSize; - var tex = Texture2D.whiteTexture; - var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), Vector2.one * 0.5f, 100, 1); - _seekBar.sprite = sprite; - _seekBar.type = Image.Type.Filled; - _seekBar.fillMethod = Image.FillMethod.Horizontal; - _seekBar.color = ForegroundColor; - - _seekCursor = new GameObject("Seek Cursor").AddComponent(); - rectTransform = _seekCursor.rectTransform; - rectTransform.SetParent(_seekBar.transform, false); - rectTransform.anchorMin = Vector2.up * 0.5f; - rectTransform.anchorMax = Vector2.up * 0.5f; - rectTransform.sizeDelta = SeekCursorSize; - _seekCursor.color = SeekCursorColor; - - _currentTime = new GameObject("Current Time").AddComponent(); - rectTransform = _currentTime.rectTransform; - rectTransform.SetParent(transform, false); - rectTransform.anchorMin = Vector2.up * 0.5f; - rectTransform.anchorMax = Vector2.up * 0.5f; - rectTransform.sizeDelta = TimeTextSize; - rectTransform.anchoredPosition = new Vector2(-(TimeTextSize.x / 2) - TimeTextMargin, 0); - _currentTime.enableAutoSizing = true; - _currentTime.fontSizeMin = 1; - _currentTime.alignment = TextAlignmentOptions.Right; - _currentTime.text = "0:00"; - - _timeLength = new GameObject("Time Length").AddComponent(); - rectTransform = _timeLength.rectTransform; - rectTransform.SetParent(transform, false); - rectTransform.anchorMin = new Vector2(1, 0.5f); - rectTransform.anchorMax = new Vector2(1, 0.5f); - rectTransform.sizeDelta = TimeTextSize; - rectTransform.anchoredPosition = new Vector2(TimeTextSize.x / 2 + TimeTextMargin, 0); - _timeLength.enableAutoSizing = true; - _timeLength.fontSizeMin = 1; - _timeLength.alignment = TextAlignmentOptions.Left; - _timeLength.text = "0:00"; - - var looperObj = new GameObject("Looper UI"); - looperObj.transform.SetParent(_seekBar.rectTransform, false); - rectTransform = looperObj.AddComponent(); - rectTransform.sizeDelta = SeekBarSize; - rectTransform.anchoredPosition = new Vector2(0, LooperUITopMargin); - _looperUI = looperObj.AddComponent(); - _looperUI.Init(this); - _looperUI.OnDragEndEvent += LooperUIOnOnDragEndEvent; - - if (_looperUI.StartTime != 0) - { - PlaybackPosition = _looperUI.StartTime; - Invoke(nameof(ApplyPlaybackPosition), 0.1f); - ApplyPlaybackPosition(); - } - - _mainCamera = Camera.main; - } - - private void LooperUIOnOnDragEndEvent() - { - PlaybackPosition = Mathf.Clamp(PlaybackPosition, _looperUI.StartTime, _looperUI.EndTime); - } - - private void OnEnable() - { - if (_songAudioSource == null || _songAudioSource.clip == null) return; - _startTimeSamples = _songAudioSource.timeSamples; - PlaybackPosition = (float) _songAudioSource.timeSamples / _songAudioSource.clip.samples; - - _timeLength.text = FormatTimeSpan(TimeSpan.FromSeconds(_songAudioSource.clip.length)); - UpdateCurrentTimeText(PlaybackPosition); - } - - private void OnDisable() - { - if (_songAudioSource == null || _songAudioSource.clip == null) return; - var newTimeSamples = Mathf.RoundToInt(Mathf.Lerp(0, _songAudioSource.clip.samples, PlaybackPosition)); - if (_startTimeSamples == newTimeSamples) return; - ApplyPlaybackPosition(); - } - - public void OnUpdate() - { - if (gameObject.activeInHierarchy || _looperUI == null || _songAudioSource == null || _songAudioSource.clip == null) return; - var newPos = (_songAudioSource.time + 0.1f) / _songAudioSource.clip.length; - if (newPos >= _looperUI.EndTime) - { - PlaybackPosition = _looperUI.StartTime; - ApplyPlaybackPosition(); - } - } - - private void LateUpdate() - { - var clampedPos = Mathf.Clamp(PlaybackPosition, _looperUI.StartTime, _looperUI.EndTime); - _seekBar.fillAmount = clampedPos; - _seekCursor.rectTransform.anchoredPosition = - new Vector2(Mathf.Lerp(0, SeekBarSize.x, clampedPos), 0); - UpdateCurrentTimeText(clampedPos); - } - - public void OnDrag(PointerEventData eventData) - { - RectTransformUtility.ScreenPointToLocalPointInRectangle(transform as RectTransform, eventData.position, - _mainCamera, out var pos); - var posX = pos.x + HalfSeekBarSize; - PlaybackPosition = Mathf.InverseLerp(0, SeekBarSize.x, posX); - - CheckLooperCursorStick(); - UpdateCurrentTimeText(PlaybackPosition); - } - - public void OnPointerDown(PointerEventData eventData) - { - eventData.useDragThreshold = false; - RectTransformUtility.ScreenPointToLocalPointInRectangle(transform as RectTransform, eventData.pressPosition, - _mainCamera, out var pos); - - if (pos.y < 0 || pos.y > SeekBarSize.y) return; - - var posX = pos.x + HalfSeekBarSize; - PlaybackPosition = Mathf.InverseLerp(0, SeekBarSize.x, posX); - - CheckLooperCursorStick(); - UpdateCurrentTimeText(PlaybackPosition); - } - - private void ApplyPlaybackPosition() - { - _songAudioSource.timeSamples = Mathf.RoundToInt(Mathf.Lerp(0, _songAudioSource.clip.samples, PlaybackPosition)); - _songAudioSource.time = _songAudioSource.time - Mathf.Min(AheadTime, _songAudioSource.time); - SongSeekBeatmapHandler.OnSongTimeChanged(_songAudioSource.time, Mathf.Min(AheadTime, _songAudioSource.time)); - } - - private void CheckLooperCursorStick() - { - if (Mathf.Abs(PlaybackPosition - _looperUI.StartTime) <= StickToLooperCursorDistance) - { - PlaybackPosition = _looperUI.StartTime; - } - else if (Mathf.Abs(PlaybackPosition - _looperUI.EndTime) <= StickToLooperCursorDistance) - { - PlaybackPosition = _looperUI.EndTime; - } - - PlaybackPosition = Mathf.Clamp(PlaybackPosition, _looperUI.StartTime, _looperUI.EndTime); - } - - private void UpdateCurrentTimeText(float playbackPos) - { - _currentTime.text = FormatTimeSpan(TimeSpan.FromSeconds(Mathf.Lerp(0, _songAudioSource.clip.length, playbackPos))); - } - - private static string FormatTimeSpan(TimeSpan ts) - { - return ts.ToString((int) ts.TotalHours > 0 ? @"h\:m\:ss" : @"m\:ss"); - } - } + public class SongSeeker : MonoBehaviour, IDragHandler, IPointerDownHandler + { + public float PlaybackPosition { get; private set; } + + [SerializeField] internal AudioSource _songAudioSource; + private LooperUI _looperUI; + + private ImageView _seekBackg; + private ImageView _seekBar; + private ImageView _seekCursor; + private TMP_Text _currentTime; + private TMP_Text _timeLength; + + private Camera _mainCamera; + + private const float AheadTime = 1f; + + public static readonly Vector2 SeekBarSize = new Vector2(100, 2); + public static readonly float HalfSeekBarSize = SeekBarSize.x / 2; + + private static readonly Vector2 ParentSize = new Vector2(100, 4); + private static readonly Color BackgroundColor = new Color(0, 0, 0, 0.25f); + private static readonly Color ForegroundColor = new Color(0.8f, 0.8f, 0.8f, 0.5f); + + private static readonly Vector2 SeekCursorSize = new Vector2(4, 4); + private static readonly Color SeekCursorColor = new Color(1, 1, 1, 0.5f); + + private static readonly Vector2 TimeTextSize = new Vector2(16, 8); + private const float TimeTextMargin = 4; + + private const float StickToLooperCursorDistance = 0.02f; + private const float LooperUITopMargin = -5f; + private bool init = false; + internal int _startTimeSamples; + private float PlayBackPosition = 0.0f; + public void Init() + { + var tex = Texture2D.whiteTexture; + var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), Vector2.one * 0.5f, 100, 1); + + + _songAudioSource = Plugin.AudioTimeSync.GetPrivateField("_audioSource"); + var rectTransform = transform as RectTransform; + rectTransform.anchorMin = Vector2.right * 0.5f; + rectTransform.anchorMax = Vector2.right * 0.5f; + rectTransform.sizeDelta = ParentSize; + rectTransform.anchoredPosition = new Vector2(0, 16); + + _seekBackg = new GameObject("Background").AddComponent(); + rectTransform = _seekBackg.rectTransform; + rectTransform.SetParent(transform, false); + rectTransform.sizeDelta = SeekBarSize + new Vector2(0, 7); + rectTransform.anchoredPosition = new Vector2(0, -1); + _seekBackg.sprite = sprite; + _seekBackg.type = Image.Type.Simple; + _seekBackg.color = BackgroundColor; + _seekBackg.material = Utilities.ImageResources.NoGlowMat; + + _seekBar = new GameObject("Seek Bar").AddComponent(); + rectTransform = _seekBar.rectTransform; + rectTransform.SetParent(transform, false); + rectTransform.sizeDelta = SeekBarSize; + + _seekBar.sprite = sprite; + _seekBar.type = Image.Type.Filled; + _seekBar.fillMethod = Image.FillMethod.Horizontal; + _seekBar.color = ForegroundColor; + _seekBar.material = Utilities.ImageResources.NoGlowMat; + + _seekCursor = new GameObject("Seek Cursor").AddComponent(); + rectTransform = _seekCursor.rectTransform; + rectTransform.SetParent(_seekBar.transform, false); + rectTransform.anchorMin = Vector2.up * 0.5f; + rectTransform.anchorMax = Vector2.up * 0.5f; + rectTransform.sizeDelta = SeekCursorSize; + + _seekCursor.sprite = sprite; + _seekCursor.type = Image.Type.Simple; + _seekCursor.color = SeekCursorColor; + _seekCursor.material = Utilities.ImageResources.NoGlowMat; + + + + + _currentTime = BeatSaberUI.CreateText(this.GetComponent(), "0:00", new Vector2(-83f, -1f)); + _currentTime.fontSize = 5f; + // rectTransform = _currentTime.rectTransform; + // rectTransform.anchorMin = Vector2.up * 0.5f; + // rectTransform.anchorMax = Vector2.up * 0.5f; + // rectTransform.sizeDelta = TimeTextSize; + // rectTransform.anchoredPosition = new Vector2(-(TimeTextSize.x / 2) - TimeTextMargin, 0); + // _currentTime.enableAutoSizing = true; + // _currentTime.fontSizeMin = 1; + _currentTime.alignment = TextAlignmentOptions.Right; + + _timeLength = BeatSaberUI.CreateText(this.GetComponent(), "0:00", new Vector2(87f, -1f)); + _timeLength.fontSize = 5f; + _timeLength.alignment = TextAlignmentOptions.Left; + + var looperObj = new GameObject("Looper UI"); + looperObj.transform.SetParent(_seekBar.rectTransform, false); + rectTransform = looperObj.AddComponent(); + rectTransform.sizeDelta = SeekBarSize; + rectTransform.anchoredPosition = new Vector2(0, LooperUITopMargin - 2f); + _looperUI = looperObj.AddComponent(); + _looperUI.Init(this); + _looperUI.OnDragEndEvent += LooperUIOnOnDragEndEvent; + + if (_looperUI.StartTime != 0) + { + PlaybackPosition = _looperUI.StartTime; + // Invoke(nameof(ApplyPlaybackPosition), 0.1f); + // ApplyPlaybackPosition(); + } + _mainCamera = Camera.main; + + } + + private void LooperUIOnOnDragEndEvent() + { + PlaybackPosition = Mathf.Clamp(PlaybackPosition, _looperUI.StartTime, _looperUI.EndTime); + } + + private void OnEnable() + { + if (_songAudioSource == null || _songAudioSource.clip == null) return; + _startTimeSamples = _songAudioSource.timeSamples; + PlaybackPosition = (float)_songAudioSource.timeSamples / _songAudioSource.clip.samples; + _timeLength.text = FormatTimeSpan(TimeSpan.FromSeconds(_songAudioSource.clip.length)); + UpdateCurrentTimeText(PlaybackPosition); + + } + + + private void OnDisable() + { + init = true; + if (_songAudioSource == null || _songAudioSource.clip == null) return; + var newTimeSamples = Mathf.RoundToInt(Mathf.Lerp(0, _songAudioSource.clip.samples, PlaybackPosition)); + if (_startTimeSamples == newTimeSamples) return; + ApplyPlaybackPosition(); + } + + public void OnUpdate() + { + if (gameObject.activeInHierarchy || _looperUI == null || _songAudioSource == null || _songAudioSource.clip == null) return; + if(!init) + { + PlaybackPosition = (float)_songAudioSource.timeSamples / _songAudioSource.clip.samples; + } + + var newPos = (_songAudioSource.time + 0.1f) / _songAudioSource.clip.length; + if (newPos >= _looperUI.EndTime && _looperUI.EndTime != 1) + { + PlaybackPosition = _looperUI.StartTime; + ApplyPlaybackPosition(); + } + } + + private void LateUpdate() + { + var clampedPos = Mathf.Clamp(PlaybackPosition, _looperUI.StartTime, _looperUI.EndTime); + _seekBar.fillAmount = clampedPos; + _seekCursor.rectTransform.anchoredPosition = + new Vector2(Mathf.Lerp(0, SeekBarSize.x, clampedPos), 0); + UpdateCurrentTimeText(clampedPos); + } + + public void OnDrag(PointerEventData eventData) + { + bool hovering = (eventData.hovered.Count > 0); + if(!hovering) { return; } + + float clampedX = Mathf.Clamp(eventData.position.x, -1.0f, 1.0f); + PlaybackPosition = (clampedX + 1f) * 0.5f; // seekbar position [0.0 - 1.0] + + CheckLooperCursorStick(); + UpdateCurrentTimeText(PlaybackPosition); + } + + public void OnPointerDown(PointerEventData eventData) + { + eventData.useDragThreshold = false; + float y = eventData.position.y - GameObject.Find("Seek Bar").transform.position.y; + if (y < -0.03f || y > 0.06) return; + + float clampedX = Mathf.Clamp(eventData.position.x, -1.0f, 1.0f); + PlaybackPosition = (clampedX + 1f) * 0.5f; // seekbar position [0.0 - 1.0] + + CheckLooperCursorStick(); + UpdateCurrentTimeText(PlaybackPosition); + } + + + public void ApplyPlaybackPosition() + { + _songAudioSource.timeSamples = Mathf.RoundToInt(Mathf.Lerp(0, _songAudioSource.clip.samples, PlaybackPosition)); + _songAudioSource.time = _songAudioSource.time - Mathf.Min(AheadTime, _songAudioSource.time); + SongSeekBeatmapHandler.OnSongTimeChanged(_songAudioSource.time, Mathf.Min(AheadTime, _songAudioSource.time)); + NoFailGameEnergy.hasFailed = false; + } + + private void CheckLooperCursorStick() + { + if (Mathf.Abs(PlaybackPosition - _looperUI.StartTime) <= StickToLooperCursorDistance) + { + PlaybackPosition = _looperUI.StartTime; + } + else if (Mathf.Abs(PlaybackPosition - _looperUI.EndTime) <= StickToLooperCursorDistance) + { + PlaybackPosition = _looperUI.EndTime; + } + + PlaybackPosition = Mathf.Clamp(PlaybackPosition, _looperUI.StartTime, _looperUI.EndTime); + } + + private void UpdateCurrentTimeText(float playbackPos) + { + _currentTime.text = FormatTimeSpan(TimeSpan.FromSeconds(Mathf.Lerp(0, _songAudioSource.clip.length, playbackPos))); + } + + private static string FormatTimeSpan(TimeSpan ts) + { + return ts.ToString((int)ts.TotalHours > 0 ? @"h\:m\:ss" : @"m\:ss"); + } + } } diff --git a/PracticePlugin/SpeedSettingsController.cs b/PracticePlugin/SpeedSettingsController.cs deleted file mode 100644 index 8568e2a..0000000 --- a/PracticePlugin/SpeedSettingsController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using UnityEngine; - -namespace PracticePlugin -{ - public class SpeedSettingsController : ListSettingsController - { - public event Action ValueChangedEvent; - - private int _indexOffset; - - protected override void GetInitValues(out int idx, out int numberOfElements) - { - _indexOffset = Plugin.NoFail ? 1 : 20; - numberOfElements = Mathf.RoundToInt(Plugin.MaxSize / Plugin.StepSize) - _indexOffset; - idx = Mathf.RoundToInt(Plugin.TimeScale / Plugin.StepSize) - _indexOffset; - } - - protected override void ApplyValue(int idx) - { - } - - protected override string TextForValue(int idx) - { - if (ValueChangedEvent != null) - { - ValueChangedEvent(Plugin.StepSize * (idx + _indexOffset)); - } - return Plugin.StepSize * 100f * (idx + _indexOffset) + "%"; - } - } -} \ No newline at end of file diff --git a/PracticePlugin/UIElementsCreator.cs b/PracticePlugin/UIElementsCreator.cs index faa2d77..7310973 100644 --- a/PracticePlugin/UIElementsCreator.cs +++ b/PracticePlugin/UIElementsCreator.cs @@ -1,91 +1,110 @@ using System; using TMPro; using UnityEngine; - +using BeatSaberMarkupLanguage; +using BeatSaberMarkupLanguage.Components; +using BeatSaberMarkupLanguage.Attributes; namespace PracticePlugin { - public class UIElementsCreator : MonoBehaviour - { - public event Action ValueChangedEvent; - public SongSeeker SongSeeker { get; private set; } - - private GameObject _speedSettings; - private TMP_Text _leaderboardText; - private float _newTimeScale = 1; + public class UIElementsCreator : MonoBehaviour + { + public event Action ValueChangedEvent; + public static SongSeeker SongSeeker { get; private set; } + internal static float defaultNJS; + internal static float defaultOffset; + internal static PracticeUI practiceUI; + internal static float _newTimeScale { get; private set; } = 1f; + + public void Init() + { + // Invoke(nameof(InitDelayed), 0.1f); + InitDelayed(); + } + + private void InitDelayed() + { + if (Plugin.PracticeMode) + { + var seekerObj = new GameObject("Song Seeker"); + seekerObj.transform.SetParent(transform, false); + seekerObj.AddComponent(); + SongSeeker = seekerObj.AddComponent(); + SongSeeker.Init(); + new GameObject("No Fail Game Energy").AddComponent(); + defaultNJS = Plugin._spawnController.GetPrivateField("_initData").noteJumpMovementSpeed; + // PracticeUI.instance.njs = defaultNJS; + // Console.WriteLine("NJS: " + UIElementsCreator.defaultNJS); + defaultOffset = BS_Utils.Plugin.LevelData.GameplayCoreSceneSetupData.difficultyBeatmap.noteJumpStartBeatOffset; + // PracticeUI.instance.offset = defaultOffset; + // Console.WriteLine("Offset: " + UIElementsCreator.defaultOffset); + } + + } + + private void OnEnable() + { + + + } + + public static void SpawnOffsetController_ValueChangedEvent(float offset) + { + // Plugin.AdjustNjsAndOffset(); + Plugin.UpdateSpawnMovementData(practiceUI.njs, practiceUI.offset); + } - public void Init() - { - Invoke(nameof(InitDelayed), 0.1f); - } + public static void NjsController_ValueChangedEvent(float njs) + { + // Plugin.AdjustNjsAndOffset(); + Plugin.UpdateSpawnMovementData(practiceUI.njs, practiceUI.offset); - private void InitDelayed() - { - if (Plugin.NoFail) - { - var seekerObj = new GameObject("Song Seeker"); - seekerObj.transform.SetParent(transform, false); - seekerObj.AddComponent(); - SongSeeker = seekerObj.AddComponent(); - SongSeeker.Init(); + } - new GameObject("No Fail Game Energy").AddComponent(); - } - else - { - if (Plugin.NoFail) return; - _leaderboardText = new GameObject("Leaderboard Text").AddComponent(); - var rectTransform = (RectTransform) _leaderboardText.transform; - rectTransform.SetParent(transform, false); - rectTransform.anchorMin = Vector2.right * 0.5f; - rectTransform.anchorMax = Vector2.right * 0.5f; - rectTransform.sizeDelta = new Vector2(100, 10); - rectTransform.anchoredPosition = new Vector2(0, 15); - _leaderboardText.fontSize = 4f; - _leaderboardText.alignment = TextAlignmentOptions.Center; + private void OnDisable() + { + if (ValueChangedEvent != null) + { + ValueChangedEvent(_newTimeScale); + } + if(SongSeeker._songAudioSource.time > 0) + { + SongSeeker._startTimeSamples = SongSeeker._songAudioSource.timeSamples - 1; + SongSeeker.ApplyPlaybackPosition(); + Plugin.TimeScale = practiceUI.speed; + } - if (Plugin.HasTimeScaleChanged) - { - _leaderboardText.text = "Leaderboard has been disabled\nSet speed to 100% and restart to enable again"; - } - } - } + // Destroy(_speedSettings); + } - private void OnEnable() - { - _speedSettings = Instantiate(Plugin.SettingsObject, transform); - _speedSettings.SetActive(true); - - var rectTransform = (RectTransform) _speedSettings.transform; - rectTransform.anchorMin = Vector2.right * 0.5f; - rectTransform.anchorMax = Vector2.right * 0.5f; - rectTransform.anchoredPosition = new Vector2(0, 10); - - var speedController = _speedSettings.GetComponent(); - speedController.ValueChangedEvent += SpeedControllerOnValueChangedEvent; - speedController.Init(); - } + private void SpeedControllerOnValueChangedEvent(float timeScale) + { + _newTimeScale = timeScale; + // njsController.Refresh(true); + // spawnOffsetController.Refresh(true); + /* + _newTimeScale = timeScale; + if (Math.Abs(_newTimeScale - 1) > 0.0000000001f) + { + // spawnOffsetController.enabled = false; + // njsController.enabled = false; - private void OnDisable() - { - if (ValueChangedEvent != null) - { - ValueChangedEvent(_newTimeScale); - } - DestroyImmediate(_speedSettings); - } + } + else + { + // spawnOffsetController.enabled = true; + // njsController.enabled = true; - private void SpeedControllerOnValueChangedEvent(float timeScale) - { - _newTimeScale = timeScale; - if (Plugin.NoFail) return; - if (!Plugin.HasTimeScaleChanged && Math.Abs(_newTimeScale - 1) > 0.0000000001f) - { - _leaderboardText.text = "Leaderboard will be disabled!"; - } - else - { - _leaderboardText.text = Plugin.HasTimeScaleChanged ? "Leaderboard has been disabled\nSet speed to 100% and restart to enable again" : string.Empty; - } - } - } + } + if (Plugin.PracticeMode) return; + if (!Plugin.HasTimeScaleChanged && Math.Abs(_newTimeScale - 1) > 0.0000000001f) + { + _leaderboardText.text = "Leaderboard will be disabled!"; + } + else + { + _leaderboardText.text = Plugin.HasTimeScaleChanged ? "Leaderboard has been disabled\nSet speed to 100% and restart to enable again" : string.Empty; + } + */ + } + } } \ No newline at end of file diff --git a/PracticePlugin/manifest.json b/PracticePlugin/manifest.json new file mode 100644 index 0000000..67cf06b --- /dev/null +++ b/PracticePlugin/manifest.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://raw.githubusercontent.com/nike4613/ModSaber-MetadataFileSchema/master/Schema.json", + "author": "Kyle1413", + "description": "Plugin for Beat Saber to control playback speed, seek through songs and set up looping sections.", + "gameVersion": "1.13.4", + "id": "PracticePlugin", + "name": "PracticePlugin", + "version": "4.12.0", + "dependsOn": { + "BSIPA": "^4.1.3", + "BS Utils": "^1.3.0", + "BeatSaberMarkupLanguage": "^1.4.0" + + }, + "links": { + "project-source": "https://github.com/Kylemc1413/PracticePlugin", + "donate": "https://ko-fi.com/kyle1413k" + } +} diff --git a/README.md b/README.md index 1c3cab8..551b874 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,30 @@ -# Practice Plugin -Plugin for Beat Saber to control playback speed, seek through songs and set up looping sections. - -## [Video Preview](https://youtu.be/2V6pyMToqYY) - -![Preview](https://i.imgur.com/Z4B8Ayc.jpg) - -# Installation -*This plugin requires IPA which you will automatically get if you install [the Song Loader Plugin](https://github.com/xyonico/BeatSaberSongLoader/releases).* - -1. Make sure that Beat Saber is not running. -2. Extract the `PracticePlugin.dll` into the `Beat Saber/Plugins` folder. - For Oculus Home: `.../Oculus Apps/Software/hyperbolic-magnetism-beat-saber` - For Steam: `.../steamapps/common/Beat Saber` - (The folder that contains Beat Saber.exe) -4. Done! You've installed the Practice Plugin. +### Plugin for Beat Saber to control playback speed, seek through songs and set up looping sections. +##### Original Plugin made by Xyonico: https://github.com/xyonico/PracticePlugin +![Preview](https://i.imgur.com/vdpHLbR.png) # Usage -You will see the speed option in the pause menu. If you turn enable No Fail, you will be able to go below 100% speed. - -The seeker is only available in No Fail mode. -Move the blue diamond below the seeker to where you want the loop to start and move the red diamond to where you want the loop to end. +- The UI shown above for the plugin will activate in practice mode, which can be accessed using the small button next to the play button +- Move the blue diamond below the seeker to where you want the loop to start and move the red diamond to where you want the loop to end. +- The Song will only loop if the red diamond is not at the end of the song +- The Speed, NJS, and spawn offset will reset to that of the song upon starting or restarting the song +- Note: If you set a starting time for the song using the built in practice mode, the map literally starts at that point, so going back to before the starting point in practice plugin won't have any notes --- - -If you need help, ask us at the Beat Saber Mod Group Discord Server: -https://discord.gg/Cz6PTM5 +## Changelog v4.2.3 +- Made Looper cursors easier to grab +## Changelog v4.2.2 +- Hopefully fixed issue with invisible notes appearing often after pausing with practice plugin +- Added back Text elements for Song Seeker +## Changelog v4.2.0 +- Update for Beat Saber 0.13.0 +## Changelog v4.1.1 +- Minor fixes +- Default NJS and Spawn Offset for the song are now underlined when they are selected in the UI +- Now Makes Use of BS Utils +## Changelog v4.0.1 +- Updated for Beat Saber 0.12.2 +- Can also change NJS and Spawn Offset from the plugin's UI +- Minor Changes +## Changelog v3.0.2 + * Fixed notes not getting despawned properly when changing timeline, causing double notes. + * Cleaned up debug logs.