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