diff --git a/.gitignore b/.gitignore index b285a22..cd68fdf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ -bin/ -obj/ -.idea/ /packages/ riderModule.iml -/_ReSharper.Caches/ \ No newline at end of file +/_ReSharper.Caches/ +.idea +.vscode +.vs +bin +obj +./BeatLeaderModifiers/BeatLeaderModifiers.csproj.user \ No newline at end of file diff --git a/BeatLeaderModifiers/BeatLeaderModifiers.csproj b/BeatLeaderModifiers/BeatLeaderModifiers.csproj index 238abe1..d301e3c 100644 --- a/BeatLeaderModifiers/BeatLeaderModifiers.csproj +++ b/BeatLeaderModifiers/BeatLeaderModifiers.csproj @@ -1,134 +1,150 @@  - - Debug - AnyCPU - 8.0.30703 - 2.0 - {A652D071-E511-4BD2-B49A-992C12385342} - Library - Properties - BeatLeaderModifiers - BeatLeaderModifiers - v4.7.2 - 512 - true - portable - ..\Refs - $(LocalRefsDir) - $(MSBuildProjectDirectory)\ - - prompt - 4 - latest - - - false - bin\Debug\ - DEBUG;TRACE - - - true - bin\Release\ - prompt - 4 - - - True - - - True - True - - - - $(BeatSaberDir)\Libs\0Harmony.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\BeatmapCore.dll - - - $(BeatSaberDir)\Plugins\BSML.dll - - - $(BeatSaberDir)\Plugins\SiraUtil.dll - - - $(BeatSaberDir)\Plugins\SongCore.dll - - - - - - - - - $(BeatSaberDir)\Beat Saber_Data\Managed\Main.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\HMLib.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\HMUI.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\IPA.Loader.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\Unity.TextMeshPro.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.AssetBundleModule.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UI.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UIElementsModule.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UIModule.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.VRModule.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\Zenject.dll - - - $(BeatSaberDir)\Beat Saber_Data\Managed\Zenject-usage.dll - - - - - - - - - - - - - - - - - - - - - - - 1.2.3 - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - + + Debug + AnyCPU + 8.0.30703 + 2.0 + {A652D071-E511-4BD2-B49A-992C12385342} + Library + Properties + BeatLeaderModifiers + BeatLeaderModifiers + v4.7.2 + 512 + true + portable + ..\Refs + $(LocalRefsDir) + $(MSBuildProjectDirectory)\ + + prompt + 4 + latest + + + false + bin\Debug\ + DEBUG;TRACE + + + true + bin\Release\ + prompt + 4 + + + True + + + True + True + + + + $(BeatSaberDir)\Libs\0Harmony.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\BeatmapCore.dll + + + $(BeatSaberDir)\Plugins\BSML.dll + + + False + $(BeatSaberDir)\Beat Saber_Data\Managed\GameplayCore.dll + False + + + $(BeatSaberDir)\Plugins\SiraUtil.dll + + + $(BeatSaberDir)\Plugins\SongCore.dll + + + + + + + + + $(BeatSaberDir)\Beat Saber_Data\Managed\Main.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\HMLib.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\HMUI.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\IPA.Loader.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\Unity.TextMeshPro.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.AssetBundleModule.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UI.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UIElementsModule.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UIModule.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.VRModule.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\Zenject.dll + + + $(BeatSaberDir)\Beat Saber_Data\Managed\Zenject-usage.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.2.3 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + \ No newline at end of file diff --git a/BeatLeaderModifiers/BeatLeaderModifiers.csproj.user b/BeatLeaderModifiers/BeatLeaderModifiers.csproj.user index d0e407e..44fc3d0 100644 --- a/BeatLeaderModifiers/BeatLeaderModifiers.csproj.user +++ b/BeatLeaderModifiers/BeatLeaderModifiers.csproj.user @@ -1,7 +1,7 @@  - 1.27.0 + 1.27.0 Ree D:\Games\Steam\steamapps\common\Beat Saber v$(BeatSaberVersion) $(BeatSaberDir)\Libs;$(BeatSaberDir)\Plugins;$(BeatSaberDir)\Beat Saber_Data\Managed; diff --git a/BeatLeaderModifiers/HarmonyPatches/CutScoreBufferPatch.cs b/BeatLeaderModifiers/HarmonyPatches/CutScoreBufferPatch.cs new file mode 100644 index 0000000..e6e1508 --- /dev/null +++ b/BeatLeaderModifiers/HarmonyPatches/CutScoreBufferPatch.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using HarmonyLib; +using IPA.Utilities; +using JetBrains.Annotations; +using UnityEngine; + +namespace BeatLeaderModifiers { + + [HarmonyPatch(typeof(CutScoreBuffer), "RefreshScores")] + internal class CutScoreBufferPatch { + + //private static float beforeCutMaxScore = 40f; + //private static float angleMaxScore = 30f; + + //private static float goodAngle = 7.5f; + //private static float badAngle = 45f; + + private static float badTiming = 0.04f; + private static float goodTiming = 0.045f; + + [UsedImplicitly] + private static void Prefix( + CutScoreBuffer __instance, + SaberSwingRatingCounter ____saberSwingRatingCounter, + int ____beforeCutScore, + NoteCutInfo ____noteCutInfo) { + + if (StandardLevelBlaBlaPatch.customCharacterisitic == CustomCharacterisitic.betterScoring) { + + //int beforeCutScore = Mathf.RoundToInt(((float)____beforeCutScore / 70.0f) * beforeCutMaxScore); + + //float angleRating = 1f - Mathf.Clamp01((Mathf.Abs(Mathf.Abs(____noteCutInfo.cutAngle) - 90f) - goodAngle) / badAngle); + //if (____noteCutInfo.noteData.cutDirection == NoteCutDirection.Any) { + // angleRating = 1f; + //} + //int angleScore = Mathf.RoundToInt(angleRating * angleMaxScore); + + //Plugin.Log.Debug(beforeCutScore + " " + angleScore); + + //float beforeCutScoreRating = (float)(beforeCutScore + angleScore) / 70f; + + + float timingRating = 1.0f - Mathf.Clamp01((Mathf.Abs(____noteCutInfo.timeDeviation) - goodTiming) / badTiming); + + ____saberSwingRatingCounter.SetField("_afterCutRating", timingRating); + ____saberSwingRatingCounter.SetField("_beforeCutRating", timingRating); + + Plugin.Log.Debug(timingRating + " "); + } + } + } +} diff --git a/BeatLeaderModifiers/HarmonyPatches/NoteControllerPatch.cs b/BeatLeaderModifiers/HarmonyPatches/NoteControllerPatch.cs new file mode 100644 index 0000000..84cff18 --- /dev/null +++ b/BeatLeaderModifiers/HarmonyPatches/NoteControllerPatch.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using HarmonyLib; +using IPA.Utilities; +using JetBrains.Annotations; +using UnityEngine; + +namespace BeatLeaderModifiers { + + [HarmonyPatch(typeof(GameNoteController), "Init")] + internal class NoteControllerPatch { + private static float colliderScale = 0.58f; + + static void ScaleColider(BoxCuttableBySaber box, float value) { + var localScale = box.transform.localScale; + + localScale.x *= value; + localScale.y *= value; + localScale.z *= value; + + box.transform.localScale = localScale; + } + + [UsedImplicitly] + private static void Postfix( + BoxCuttableBySaber[] ____bigCuttableBySaberList, + BoxCuttableBySaber[] ____smallCuttableBySaberList) { + + if (StandardLevelBlaBlaPatch.customCharacterisitic == CustomCharacterisitic.betterScoring) { + foreach (var item in ____bigCuttableBySaberList) + { + ScaleColider(item, colliderScale); + } + foreach (var item in ____smallCuttableBySaberList) + { + ScaleColider(item, colliderScale); + } + } + } + } +} diff --git a/BeatLeaderModifiers/HarmonyPatches/SetContentPatch.cs b/BeatLeaderModifiers/HarmonyPatches/SetContentPatch.cs index 90ea3e9..66d80ae 100644 --- a/BeatLeaderModifiers/HarmonyPatches/SetContentPatch.cs +++ b/BeatLeaderModifiers/HarmonyPatches/SetContentPatch.cs @@ -12,7 +12,7 @@ internal class SetContentPatch { [UsedImplicitly] private static void Prefix(IBeatmapLevel level) { - if (!level.levelID.StartsWith("custom_level")) return; + if (!level.levelID.StartsWith("custom_level") || !(level.beatmapLevelData is BeatmapLevelData)) return; if (level.beatmapLevelData.difficultyBeatmapSets.Any(x => x.beatmapCharacteristic.serializedName.Equals(CharacteristicsManager.BetterScoringCharacteristic.SerializedName))) { @@ -32,6 +32,8 @@ private static async void AddCustomCharacteristic(IBeatmapLevel level) { var difficultyBeatmapSets = new List(level.beatmapLevelData.difficultyBeatmapSets); foreach (var originalBeatmapSet in level.beatmapLevelData.difficultyBeatmapSets) { + if (originalBeatmapSet.beatmapCharacteristic.serializedName != "Standard") continue; + var beatmapSet = new CustomDifficultyBeatmapSet(characteristicSO); var customDifficulties = await CreateCustomDifficulties(originalBeatmapSet.difficultyBeatmaps, beatmapSet); beatmapSet.SetCustomDifficultyBeatmaps(customDifficulties); diff --git a/BeatLeaderModifiers/HarmonyPatches/StandardLevelBlaBlaPatch.cs b/BeatLeaderModifiers/HarmonyPatches/StandardLevelBlaBlaPatch.cs new file mode 100644 index 0000000..ab1ea51 --- /dev/null +++ b/BeatLeaderModifiers/HarmonyPatches/StandardLevelBlaBlaPatch.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using HarmonyLib; +using IPA.Utilities; +using JetBrains.Annotations; +using static PlayerSaveData; + +enum CustomCharacterisitic { + standard = 0, + betterScoring = 1 +} + +namespace BeatLeaderModifiers { + [HarmonyPatch(typeof(StandardLevelScenesTransitionSetupDataSO), nameof(StandardLevelScenesTransitionSetupDataSO.Init))] + class StandardLevelBlaBlaPatch + { + public static CustomCharacterisitic customCharacterisitic; + static void Prefix(StandardLevelScenesTransitionSetupDataSO __instance, string gameMode, IDifficultyBeatmap difficultyBeatmap, IPreviewBeatmapLevel previewBeatmapLevel, OverrideEnvironmentSettings overrideEnvironmentSettings, + GameplayModifiers gameplayModifiers, ColorScheme overrideColorScheme, PlayerSpecificSettings playerSpecificSettings, ref PracticeSettings practiceSettings, string backButtonText, bool useTestNoteCutSoundEffects) + { + if (difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName == CharacteristicsManager.BetterScoringCharacteristic.SerializedName) { + customCharacterisitic = CustomCharacterisitic.betterScoring; + } else { + customCharacterisitic = CustomCharacterisitic.standard; + } + } + } +} diff --git a/BeatLeaderModifiers/Icons/RhythmGame.png b/BeatLeaderModifiers/Icons/RhythmGame.png new file mode 100644 index 0000000..b8a55b4 Binary files /dev/null and b/BeatLeaderModifiers/Icons/RhythmGame.png differ diff --git a/BeatLeaderModifiers/Installers/OnGameplayCoreInstaller.cs b/BeatLeaderModifiers/Installers/OnGameplayCoreInstaller.cs index b435abe..268123a 100644 --- a/BeatLeaderModifiers/Installers/OnGameplayCoreInstaller.cs +++ b/BeatLeaderModifiers/Installers/OnGameplayCoreInstaller.cs @@ -5,7 +5,12 @@ namespace BeatLeaderModifiers.Installers { [UsedImplicitly] public class OnGameplayCoreInstaller : Installer { public override void InstallBindings() { - Plugin.Log.Debug("OnGameplayCoreInstaller"); + var difficultyBeatmap = Container.TryResolve(); + var characteristic = difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic; + + if (characteristic.serializedName == CharacteristicsManager.BetterScoringCharacteristic.SerializedName) { + Container.BindInterfacesTo().AsSingle(); + } } } } \ No newline at end of file diff --git a/BeatLeaderModifiers/Managers/BeatLeaderInteropManager.cs b/BeatLeaderModifiers/Managers/BeatLeaderInteropManager.cs new file mode 100644 index 0000000..0841e7f --- /dev/null +++ b/BeatLeaderModifiers/Managers/BeatLeaderInteropManager.cs @@ -0,0 +1,31 @@ +using System.Reflection; +using HarmonyLib; +using IPA.Loader; + +namespace BeatLeaderModifiers; + +internal static class BeatLeaderInteropManager { + private static Harmony _harmony; + + public static void ApplyPatches() { + var plugin = PluginManager.GetPluginFromId("BeatLeader"); + if (plugin == null) return; + + var assembly = plugin.Assembly; + var swingRatingEnhancerType = assembly.GetType("BeatLeader.Core.Managers.NoteEnhancer.SwingRatingEnhancer"); + + var patch = new HarmonyPatchDescriptor( + swingRatingEnhancerType.GetMethod("ChooseSwingRating", BindingFlags.Static | BindingFlags.NonPublic), + typeof(BeatLeaderInteropManager).GetMethod(nameof(ChooseSwingRating), BindingFlags.Static | BindingFlags.NonPublic) + ); + + _harmony = new Harmony(nameof(BeatLeaderInteropManager)); + _harmony.Patch(patch); + } + + private static bool ChooseSwingRating(float real, float unclamped, ref float __result) { + if (StandardLevelBlaBlaPatch.customCharacterisitic != CustomCharacterisitic.betterScoring) return true; + __result = real; + return false; + } +} \ No newline at end of file diff --git a/BeatLeaderModifiers/Managers/CharacteristicsManager.cs b/BeatLeaderModifiers/Managers/CharacteristicsManager.cs index eea0645..059fda6 100644 --- a/BeatLeaderModifiers/Managers/CharacteristicsManager.cs +++ b/BeatLeaderModifiers/Managers/CharacteristicsManager.cs @@ -10,10 +10,10 @@ internal static class CharacteristicsManager { #region Characteritics public static readonly CharacteristicDescriptor BetterScoringCharacteristic = new( - "BeatLeaderModifiers.Resources.TestIcon.png", - "BetterScoring", - "BL_BS", - "useful hint" + "BeatLeaderModifiers.Icons.RhythmGame.png", + "RhythmGameStandard", + "RhythmGameStandard", + "It's a rhythm game!" ); #endregion @@ -25,35 +25,13 @@ public static void RegisterCharacteristics() { } private static void RegisterCustomCharacteristic(CharacteristicDescriptor characteristicDescriptor) { - Collections.RegisterCustomCharacteristic( + characteristicDescriptor.CharacteristicSO = Collections.RegisterCustomCharacteristic( characteristicDescriptor.Icon, characteristicDescriptor.Name, characteristicDescriptor.HintText, characteristicDescriptor.SerializedName, characteristicDescriptor.Name ); - - characteristicDescriptor.CharacteristicSO = CreateCharacteristicSO(characteristicDescriptor); - } - - #endregion - - #region CreateCharacteristicSO - - private static BeatmapCharacteristicSO CreateCharacteristicSO(CharacteristicDescriptor characteristicDescriptor) { - var beatmapCharacteristicSO = ScriptableObject.CreateInstance(); - - beatmapCharacteristicSO.SetField("_icon", characteristicDescriptor.Icon); - beatmapCharacteristicSO.SetField("_characteristicNameLocalizationKey", characteristicDescriptor.Name); - beatmapCharacteristicSO.SetField("_descriptionLocalizationKey", characteristicDescriptor.HintText); - beatmapCharacteristicSO.SetField("_serializedName", characteristicDescriptor.SerializedName); - beatmapCharacteristicSO.SetField("_compoundIdPartName", characteristicDescriptor.Name); - beatmapCharacteristicSO.SetField("_sortingOrder", 100); - beatmapCharacteristicSO.SetField("_containsRotationEvents", false); - beatmapCharacteristicSO.SetField("_requires360Movement", false); - beatmapCharacteristicSO.SetField("_numberOfColors", 2); - - return beatmapCharacteristicSO; } #endregion @@ -73,7 +51,7 @@ public CharacteristicDescriptor(string icon, string name, string serializedName, HintText = hintText; //TODO: AssetBundle? - Icon = Resources.FindObjectsOfTypeAll().FirstOrDefault(it => it.name == icon); + Icon = SongCoreUtilities.LoadSpriteFromResources(icon); } } diff --git a/BeatLeaderModifiers/Managers/CutInterpolationManager.cs b/BeatLeaderModifiers/Managers/CutInterpolationManager.cs new file mode 100644 index 0000000..699fe07 --- /dev/null +++ b/BeatLeaderModifiers/Managers/CutInterpolationManager.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using HarmonyLib; +using JetBrains.Annotations; +using UnityEngine; +using Zenject; + +namespace BeatLeaderModifiers; + +[UsedImplicitly] +internal class CutInterpolationManager : IInitializable, IDisposable, ILateTickable { + #region Harmony + + private static Harmony _harmony; + private static CutInterpolationManager _instance; + + private void ApplyPatches() { + _harmony = new Harmony(nameof(CutInterpolationManager)); + _instance = this; + + _harmony.Patch(NoteWasCutEventPatch); + } + + private static void RemovePatches() { + _harmony.UnpatchSelf(); + _harmony = default; + _instance = default; + } + + #endregion + + #region NoteWasCutEventPatch + + private static HarmonyPatchDescriptor NoteWasCutEventPatch => new( + typeof(NoteController).GetMethod("SendNoteWasCutEvent", BindingFlags.Instance | BindingFlags.NonPublic), + typeof(CutInterpolationManager).GetMethod(nameof(OnSendNoteWasCutEvent), BindingFlags.Static | BindingFlags.NonPublic) + ); + + // ReSharper disable InconsistentNaming + private static bool OnSendNoteWasCutEvent( + NoteController __instance, + NoteCutInfo noteCutInfo, + LazyCopyHashSet ____noteWasCutEvent + ) { + _instance.OnBeforeNoteWasCutEvent(__instance, ref noteCutInfo); + foreach (var controllerNoteWasCutEvent in ____noteWasCutEvent.items) + controllerNoteWasCutEvent.HandleNoteControllerNoteWasCut(__instance, in noteCutInfo); + return false; + } + + #endregion + + #region Initialize/Dispose + + [Inject, UsedImplicitly] + private BeatmapObjectManager _beatmapObjectManager; + + [Inject, UsedImplicitly] + private AudioTimeSyncController _audioTimeSyncController; + + public void Initialize() { + ApplyPatches(); + _beatmapObjectManager.noteWasSpawnedEvent += OnNoteWasSpawned; + _beatmapObjectManager.noteWasDespawnedEvent += OnNoteWasDespawned; + } + + public void Dispose() { + RemovePatches(); + _beatmapObjectManager.noteWasSpawnedEvent -= OnNoteWasSpawned; + _beatmapObjectManager.noteWasDespawnedEvent -= OnNoteWasDespawned; + } + + #endregion + + #region NoteMovementTracker + + private readonly HashSet _notesCache = new(10); + private readonly Dictionary _noteMovementCache = new(10); + + public void LateTick() { + var songTime = _audioTimeSyncController.songTime; + + foreach (var noteController in _notesCache) { + _noteMovementCache[noteController] = new NoteMovementData( + songTime, + noteController.transform.position + ); + } + } + + private void OnNoteWasSpawned(NoteController noteController) { + _noteMovementCache[noteController] = default; + _notesCache.Add(noteController); + } + + private void OnNoteWasDespawned(NoteController noteController) { + _noteMovementCache.Remove(noteController); + _notesCache.Remove(noteController); + } + + private struct NoteMovementData { + public readonly float SongTime; + public readonly Vector3 NotePosition; + + public NoteMovementData(float songTime, Vector3 notePosition) { + SongTime = songTime; + NotePosition = notePosition; + } + } + + #endregion + + #region Events + + private static float badTiming = 0.04f; + private static float goodTiming = 0.045f; + + private void OnBeforeNoteWasCutEvent(NoteController noteController, ref NoteCutInfo noteCutInfo) { + if (!_noteMovementCache.ContainsKey(noteController)) return; + var previousNoteMovementData = _noteMovementCache[noteController]; + var currentNotePosition = noteCutInfo.notePosition; + + var saberMovementData = (SaberMovementData)noteCutInfo.saberMovementData; + var previousBladeData = saberMovementData.prevAddedData; + var currentBladeData = saberMovementData.lastAddedData; + + var previousFrameData = new InterpolationUtils.FrameData( + previousNoteMovementData.SongTime, + previousBladeData.bottomPos, + previousBladeData.topPos - previousBladeData.bottomPos, + previousNoteMovementData.NotePosition + ); + + var currentFrameData = new InterpolationUtils.FrameData( + _audioTimeSyncController.songTime, + currentBladeData.bottomPos, + currentBladeData.topPos - currentBladeData.bottomPos, + currentNotePosition + ); + + InterpolationUtils.CalculateClosestApproach(previousFrameData, currentFrameData, out var newTime, out var newDistance); + var newTimeDeviation = noteCutInfo.noteData.time - newTime; + + Plugin.Log.Debug("" + noteCutInfo.timeDeviation + " " + newTimeDeviation); + Plugin.Log.Debug("" + currentNotePosition.x + " " + currentNotePosition.y + " " + currentNotePosition.z + " " + previousNoteMovementData.NotePosition.x + " " + previousNoteMovementData.NotePosition.y + " " + previousNoteMovementData.NotePosition.z); + + float timingRating = Mathf.Clamp01((Mathf.Abs(newTimeDeviation) - goodTiming) / badTiming); + + noteCutInfo = new NoteCutInfo( + noteCutInfo.noteData, + noteCutInfo.speedOK, + noteCutInfo.directionOK, + noteCutInfo.saberTypeOK, + noteCutInfo.wasCutTooSoon, + noteCutInfo.saberSpeed, + noteCutInfo.saberDir, + noteCutInfo.saberType, + newTimeDeviation, + noteCutInfo.cutDirDeviation, + noteCutInfo.cutPoint, + noteCutInfo.cutNormal, + timingRating * 3, + noteCutInfo.cutAngle, + noteCutInfo.worldRotation, + noteCutInfo.inverseWorldRotation, + noteCutInfo.noteRotation, + noteCutInfo.notePosition, + noteCutInfo.saberMovementData + ); + } + + #endregion +} \ No newline at end of file diff --git a/BeatLeaderModifiers/Managers/SongCoreInteropManager.cs b/BeatLeaderModifiers/Managers/SongCoreInteropManager.cs new file mode 100644 index 0000000..2f9e3d8 --- /dev/null +++ b/BeatLeaderModifiers/Managers/SongCoreInteropManager.cs @@ -0,0 +1,46 @@ +using System.Linq; +using System.Reflection; +using HarmonyLib; +using IPA.Loader; +using SongCore; +using SongCore.Data; +using SongCore.Utilities; + +namespace BeatLeaderModifiers; + +internal static class SongCoreInteropManager { + private static Harmony _harmony; + + public static void ApplyPatches() { + var plugin = PluginManager.GetPluginFromId("SongCore"); + if (plugin == null) return; + + var assembly = plugin.Assembly; + var swingRatingEnhancerType = assembly.GetType("SongCore.Collections"); + + var patch = new HarmonyPatchDescriptor( + swingRatingEnhancerType.GetMethod("RetrieveDifficultyData", BindingFlags.Static | BindingFlags.Public), + typeof(SongCoreInteropManager).GetMethod(nameof(RetrieveDifficultyData), BindingFlags.Static | BindingFlags.Public) + ); + + _harmony = new Harmony(nameof(SongCoreInteropManager)); + _harmony.Patch(patch); + } + + public static bool RetrieveDifficultyData(IDifficultyBeatmap beatmap, ref ExtraSongData.DifficultyData? __result) { + ExtraSongData? songData = null; + + if (beatmap.level is CustomPreviewBeatmapLevel customLevel) + { + songData = Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(customLevel)); + } + + __result = songData?._difficulties.FirstOrDefault(x => + x._difficulty == beatmap.difficulty && + (x._beatmapCharacteristicName == beatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.characteristicNameLocalizationKey || + x._beatmapCharacteristicName == beatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName || + (x._beatmapCharacteristicName == "Standard" && beatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName == CharacteristicsManager.BetterScoringCharacteristic.SerializedName))); + + return false; + } +} \ No newline at end of file diff --git a/BeatLeaderModifiers/Plugin.cs b/BeatLeaderModifiers/Plugin.cs index 48988f6..041de76 100644 --- a/BeatLeaderModifiers/Plugin.cs +++ b/BeatLeaderModifiers/Plugin.cs @@ -27,6 +27,8 @@ public Plugin(IPALogger logger, Zenjector zenjector) { [OnStart, UsedImplicitly] public void OnApplicationStart() { HarmonyHelper.ApplyPatches(); + BeatLeaderInteropManager.ApplyPatches(); + SongCoreInteropManager.ApplyPatches(); CharacteristicsManager.RegisterCharacteristics(); } diff --git a/BeatLeaderModifiers/Utils/HarmonyUtils.cs b/BeatLeaderModifiers/Utils/HarmonyUtils.cs new file mode 100644 index 0000000..37024d1 --- /dev/null +++ b/BeatLeaderModifiers/Utils/HarmonyUtils.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using HarmonyLib; +using JetBrains.Annotations; + +namespace BeatLeaderModifiers { + internal static class HarmonyUtils { + public static void Patch(this Harmony harmony, HarmonyPatchDescriptor patchDescriptor) { + harmony.Patch(patchDescriptor.Original, patchDescriptor.Prefix, patchDescriptor.Postfix); + } + } + + internal class HarmonyPatchDescriptor { + public readonly MethodInfo Original; + [CanBeNull] public readonly HarmonyMethod Prefix; + [CanBeNull] public readonly HarmonyMethod Postfix; + + public HarmonyPatchDescriptor( + MethodInfo original, + [CanBeNull] MethodInfo prefix = null, + [CanBeNull] MethodInfo postfix = null + ) { + Original = original; + Prefix = prefix == null ? null : new HarmonyMethod(prefix); + Postfix = postfix == null ? null : new HarmonyMethod(postfix); + } + } +} \ No newline at end of file diff --git a/BeatLeaderModifiers/Utils/InterpolationUtils.cs b/BeatLeaderModifiers/Utils/InterpolationUtils.cs new file mode 100644 index 0000000..609e03c --- /dev/null +++ b/BeatLeaderModifiers/Utils/InterpolationUtils.cs @@ -0,0 +1,60 @@ +using UnityEngine; + +namespace BeatLeaderModifiers; + +public static class InterpolationUtils { + #region FrameData + + public readonly struct FrameData { + public readonly float Time; + public readonly Vector3 SaberPosition; + public readonly Vector3 SaberDirection; + public readonly Vector3 NotePosition; + + public FrameData(float time, Vector3 saberPosition, Vector3 saberDirection, Vector3 notePosition) { + Time = time; + SaberPosition = saberPosition; + SaberDirection = saberDirection; + NotePosition = notePosition; + } + + public static FrameData Lerp(FrameData a, FrameData b, float t) { + return new FrameData( + Mathf.LerpUnclamped(a.Time, b.Time, t), + Vector3.LerpUnclamped(a.SaberPosition, b.SaberPosition, t), + Vector3.LerpUnclamped(a.SaberDirection, b.SaberDirection, t).normalized, + Vector3.LerpUnclamped(a.NotePosition, b.NotePosition, t) + ); + } + } + + #endregion + + #region CalculateClosestApproach + + private const int Resolution = 200; + + public static void CalculateClosestApproach(FrameData a, FrameData b, out float time, out float distance) { + time = 0.0f; + distance = float.MaxValue; + + for (var i = 0; i <= Resolution; i++) { + var t = (float)i / (Resolution); + t = -1 + 3 * t; + + var f = FrameData.Lerp(a, b, t); + var d = GetDistance(f.SaberPosition, f.SaberDirection, f.NotePosition); + if (d >= distance) continue; + time = f.Time; + distance = d; + } + } + + private static float GetDistance(Vector3 lineFrom, Vector3 lineDirection, Vector3 point) { + var v = point - lineFrom; + var angle = Vector3.Angle(v, lineDirection) * Mathf.Deg2Rad; + return Mathf.Sin(angle) * v.magnitude; + } + + #endregion +} \ No newline at end of file diff --git a/BeatLeaderModifiers/manifest.json b/BeatLeaderModifiers/manifest.json index b68aa24..6de97bc 100644 --- a/BeatLeaderModifiers/manifest.json +++ b/BeatLeaderModifiers/manifest.json @@ -10,5 +10,8 @@ "BSIPA": "^4.2.2", "SiraUtil": "^3.1.0" }, + "loadAfter": [ + "BeatLeader" + ], "features": [] } \ No newline at end of file