diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b285a22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin/ +obj/ +.idea/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/BeatLeaderModifiers.sln b/BeatLeaderModifiers.sln new file mode 100644 index 0000000..80c784b --- /dev/null +++ b/BeatLeaderModifiers.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31424.327 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeatLeaderModifiers", "BeatLeaderModifiers\BeatLeaderModifiers.csproj", "{A652D071-E511-4BD2-B49A-992C12385342}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A652D071-E511-4BD2-B49A-992C12385342}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A652D071-E511-4BD2-B49A-992C12385342}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A652D071-E511-4BD2-B49A-992C12385342}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A652D071-E511-4BD2-B49A-992C12385342}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {63CBE1E9-6AEF-4CE5-9EEA-7FA7DB3AEBCF} + EndGlobalSection +EndGlobal diff --git a/BeatLeaderModifiers/BeatLeaderModifiers.csproj b/BeatLeaderModifiers/BeatLeaderModifiers.csproj new file mode 100644 index 0000000..238abe1 --- /dev/null +++ b/BeatLeaderModifiers/BeatLeaderModifiers.csproj @@ -0,0 +1,134 @@ + + + + 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 + + + + \ No newline at end of file diff --git a/BeatLeaderModifiers/BeatLeaderModifiers.csproj.DotSettings b/BeatLeaderModifiers/BeatLeaderModifiers.csproj.DotSettings new file mode 100644 index 0000000..490218d --- /dev/null +++ b/BeatLeaderModifiers/BeatLeaderModifiers.csproj.DotSettings @@ -0,0 +1,37 @@ + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + False + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True \ No newline at end of file diff --git a/BeatLeaderModifiers/BeatLeaderModifiers.csproj.user b/BeatLeaderModifiers/BeatLeaderModifiers.csproj.user new file mode 100644 index 0000000..d0e407e --- /dev/null +++ b/BeatLeaderModifiers/BeatLeaderModifiers.csproj.user @@ -0,0 +1,8 @@ + + + + 1.27.0 + D:\Games\Steam\steamapps\common\Beat Saber v$(BeatSaberVersion) + $(BeatSaberDir)\Libs;$(BeatSaberDir)\Plugins;$(BeatSaberDir)\Beat Saber_Data\Managed; + + \ No newline at end of file diff --git a/BeatLeaderModifiers/Directory.Build.props b/BeatLeaderModifiers/Directory.Build.props new file mode 100644 index 0000000..02c5dc1 --- /dev/null +++ b/BeatLeaderModifiers/Directory.Build.props @@ -0,0 +1,14 @@ + + + + + true + true + true + + + false + true + true + + \ No newline at end of file diff --git a/BeatLeaderModifiers/Directory.Build.targets b/BeatLeaderModifiers/Directory.Build.targets new file mode 100644 index 0000000..1c2dd75 --- /dev/null +++ b/BeatLeaderModifiers/Directory.Build.targets @@ -0,0 +1,96 @@ + + + + + 2.0 + + false + + $(OutputPath)$(AssemblyName) + + $(OutputPath)Final + True + + + + + + + + + + + + + + + + + + + + + + $(AssemblyName) + $(ArtifactName)-$(PluginVersion) + $(ArtifactName)-bs$(GameVersion) + $(ArtifactName)-$(CommitHash) + + + + + + + $(AssemblyName) + + + + + + + + + + + + + + $(AssemblyName) + $(OutDir)zip\ + + + + + + + + + + + + + $(BeatSaberDir)\Plugins + True + Unable to copy assembly to game folder, did you set 'BeatSaberDir' correctly in your 'csproj.user' file? Plugins folder doesn't exist: '$(PluginDir)'. + + Unable to copy to Plugins folder, '$(BeatSaberDir)' does not appear to be a Beat Saber game install. + + Unable to copy to Plugins folder, 'BeatSaberDir' has not been set in your 'csproj.user' file. + False + + + + + + + + $(BeatSaberDir)\IPA\Pending\Plugins + + + + + + + + \ No newline at end of file diff --git a/BeatLeaderModifiers/HarmonyPatches/HarmonyHelper.cs b/BeatLeaderModifiers/HarmonyPatches/HarmonyHelper.cs new file mode 100644 index 0000000..f93378d --- /dev/null +++ b/BeatLeaderModifiers/HarmonyPatches/HarmonyHelper.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using HarmonyLib; + +namespace BeatLeaderModifiers { + public static class HarmonyHelper { + private static Harmony _harmony; + + private static bool _initialized; + + private static void LazyInit() { + if (_initialized) return; + _harmony = new Harmony("BeatLeaderModifiers"); + _initialized = true; + } + + public static void ApplyPatches() { + LazyInit(); + _harmony.PatchAll(Assembly.GetExecutingAssembly()); + } + + public static void RemovePatches() { + if (!_initialized) return; + _harmony.UnpatchSelf(); + } + } +} \ No newline at end of file diff --git a/BeatLeaderModifiers/HarmonyPatches/SetContentPatch.cs b/BeatLeaderModifiers/HarmonyPatches/SetContentPatch.cs new file mode 100644 index 0000000..90ea3e9 --- /dev/null +++ b/BeatLeaderModifiers/HarmonyPatches/SetContentPatch.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using HarmonyLib; +using IPA.Utilities; +using JetBrains.Annotations; + +namespace BeatLeaderModifiers { + [HarmonyPatch(typeof(StandardLevelDetailView), "SetContent")] + internal class SetContentPatch { + #region Prefix + + [UsedImplicitly] + private static void Prefix(IBeatmapLevel level) { + if (!level.levelID.StartsWith("custom_level")) return; + + if (level.beatmapLevelData.difficultyBeatmapSets.Any(x => + x.beatmapCharacteristic.serializedName.Equals(CharacteristicsManager.BetterScoringCharacteristic.SerializedName))) { + return; //TODO: Filter all custom characteristics + } + + AddCustomCharacteristic(level); + } + + #endregion + + #region AddCustomCharacteristic + + private static async void AddCustomCharacteristic(IBeatmapLevel level) { + var characteristicSO = CharacteristicsManager.BetterScoringCharacteristic.CharacteristicSO; + + var difficultyBeatmapSets = new List(level.beatmapLevelData.difficultyBeatmapSets); + + foreach (var originalBeatmapSet in level.beatmapLevelData.difficultyBeatmapSets) { + var beatmapSet = new CustomDifficultyBeatmapSet(characteristicSO); + var customDifficulties = await CreateCustomDifficulties(originalBeatmapSet.difficultyBeatmaps, beatmapSet); + beatmapSet.SetCustomDifficultyBeatmaps(customDifficulties); + + if (beatmapSet.difficultyBeatmaps.Count > 0) { + difficultyBeatmapSets.Add(beatmapSet); + } + } + + ((BeatmapLevelData)level.beatmapLevelData).SetField>("_difficultyBeatmapSets", difficultyBeatmapSets); + } + + #endregion + + #region CreateCustomDifficulties + + private static async Task CreateCustomDifficulties( + IEnumerable difficultyBeatmaps, + IDifficultyBeatmapSet beatmapSet + ) { + var customDifficulties = new List(); + + foreach (var difficultyBeatmap in difficultyBeatmaps) { + var beatmapDataBasicInfo = await difficultyBeatmap.GetBeatmapDataBasicInfoAsync(); + var customBeatmap = new CustomDifficultyBeatmap( + difficultyBeatmap.level, + beatmapSet, + difficultyBeatmap.difficulty, + difficultyBeatmap.difficultyRank, + difficultyBeatmap.noteJumpMovementSpeed, + difficultyBeatmap.noteJumpStartBeatOffset, + difficultyBeatmap.level.beatsPerMinute, + ((CustomDifficultyBeatmap)difficultyBeatmap).beatmapSaveData, + beatmapDataBasicInfo + ); + + customDifficulties.Add(customBeatmap); + } + + return customDifficulties.ToArray(); + } + } + + #endregion +} \ No newline at end of file diff --git a/BeatLeaderModifiers/Installers/OnAppInitInstaller.cs b/BeatLeaderModifiers/Installers/OnAppInitInstaller.cs new file mode 100644 index 0000000..05ebf97 --- /dev/null +++ b/BeatLeaderModifiers/Installers/OnAppInitInstaller.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using Zenject; + +namespace BeatLeaderModifiers.Installers { + [UsedImplicitly] + public class OnAppInitInstaller : Installer { + public override void InstallBindings() { + Plugin.Log.Debug("OnAppInitInstaller"); + } + } +} \ No newline at end of file diff --git a/BeatLeaderModifiers/Installers/OnGameplayCoreInstaller.cs b/BeatLeaderModifiers/Installers/OnGameplayCoreInstaller.cs new file mode 100644 index 0000000..b435abe --- /dev/null +++ b/BeatLeaderModifiers/Installers/OnGameplayCoreInstaller.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using Zenject; + +namespace BeatLeaderModifiers.Installers { + [UsedImplicitly] + public class OnGameplayCoreInstaller : Installer { + public override void InstallBindings() { + Plugin.Log.Debug("OnGameplayCoreInstaller"); + } + } +} \ No newline at end of file diff --git a/BeatLeaderModifiers/Installers/OnMenuInstaller.cs b/BeatLeaderModifiers/Installers/OnMenuInstaller.cs new file mode 100644 index 0000000..4a392b9 --- /dev/null +++ b/BeatLeaderModifiers/Installers/OnMenuInstaller.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using Zenject; + +namespace BeatLeaderModifiers.Installers { + [UsedImplicitly] + public class OnMenuInstaller : Installer { + public override void InstallBindings() { + Plugin.Log.Debug("OnMenuInstaller"); + } + } +} \ No newline at end of file diff --git a/BeatLeaderModifiers/Managers/CharacteristicsManager.cs b/BeatLeaderModifiers/Managers/CharacteristicsManager.cs new file mode 100644 index 0000000..eea0645 --- /dev/null +++ b/BeatLeaderModifiers/Managers/CharacteristicsManager.cs @@ -0,0 +1,81 @@ +using System.Linq; +using IPA.Utilities; +using SongCore; +using UnityEngine; +using SongCoreUtilities = SongCore.Utilities.Utils; + +namespace BeatLeaderModifiers; + +internal static class CharacteristicsManager { + #region Characteritics + + public static readonly CharacteristicDescriptor BetterScoringCharacteristic = new( + "BeatLeaderModifiers.Resources.TestIcon.png", + "BetterScoring", + "BL_BS", + "useful hint" + ); + + #endregion + + #region RegisterCharacteristics + + public static void RegisterCharacteristics() { + RegisterCustomCharacteristic(BetterScoringCharacteristic); + } + + private static void RegisterCustomCharacteristic(CharacteristicDescriptor characteristicDescriptor) { + 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 + + #region CharacteristicDescriptor + + public class CharacteristicDescriptor { + public readonly Sprite Icon; + public readonly string Name; + public readonly string SerializedName; + public readonly string HintText; + public BeatmapCharacteristicSO CharacteristicSO = default; + + public CharacteristicDescriptor(string icon, string name, string serializedName, string hintText) { + Name = name; + SerializedName = serializedName; + HintText = hintText; + + //TODO: AssetBundle? + Icon = Resources.FindObjectsOfTypeAll().FirstOrDefault(it => it.name == icon); + } + } + + #endregion +} \ No newline at end of file diff --git a/BeatLeaderModifiers/Plugin.cs b/BeatLeaderModifiers/Plugin.cs new file mode 100644 index 0000000..48988f6 --- /dev/null +++ b/BeatLeaderModifiers/Plugin.cs @@ -0,0 +1,42 @@ +using BeatLeaderModifiers.Installers; +using IPA; +using JetBrains.Annotations; +using SiraUtil.Zenject; +using IPALogger = IPA.Logging.Logger; + +namespace BeatLeaderModifiers { + [Plugin(RuntimeOptions.SingleStartInit)] + [UsedImplicitly] + public class Plugin { + #region Init + + internal static IPALogger Log { get; private set; } + + [Init] + public Plugin(IPALogger logger, Zenjector zenjector) { + Log = logger; + zenjector.Install(Location.App); + zenjector.Install(Location.Menu); + zenjector.Install(Location.GameCore); + } + + #endregion + + #region OnApplicationStart + + [OnStart, UsedImplicitly] + public void OnApplicationStart() { + HarmonyHelper.ApplyPatches(); + CharacteristicsManager.RegisterCharacteristics(); + } + + #endregion + + #region OnApplicationQuit + + [OnExit, UsedImplicitly] + public void OnApplicationQuit() { } + + #endregion + } +} \ No newline at end of file diff --git a/BeatLeaderModifiers/Properties/AssemblyInfo.cs b/BeatLeaderModifiers/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..64dc9fa --- /dev/null +++ b/BeatLeaderModifiers/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BeatLeaderModifiers")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BeatLeaderModifiers")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("958b345b-4ae1-404b-bfbc-2555f05e74f1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("0.0.1")] +[assembly: AssemblyFileVersion("0.0.1")] \ No newline at end of file diff --git a/BeatLeaderModifiers/manifest.json b/BeatLeaderModifiers/manifest.json new file mode 100644 index 0000000..b68aa24 --- /dev/null +++ b/BeatLeaderModifiers/manifest.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://raw.githubusercontent.com/bsmg/BSIPA-MetadataFileSchema/master/Schema.json", + "id": "BeatLeaderModifiers", + "name": "BeatLeaderModifiers", + "author": "", + "version": "0.0.1", + "description": "", + "gameVersion": "1.27.0", + "dependsOn": { + "BSIPA": "^4.2.2", + "SiraUtil": "^3.1.0" + }, + "features": [] +} \ No newline at end of file