├── versions.txt ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── Tools └── BiendeoCHLibValidator │ ├── packages.config │ ├── App.config │ ├── IValidator.cs │ ├── WrapperValidator.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── BiendeoCHLibValidator.cs │ └── BiendeoCHLibValidator.csproj ├── .editorconfig ├── BiendeoCHLib ├── Settings │ ├── IGUIConfigurable.cs │ ├── GUIConfigurationStyles.cs │ ├── FormattableColorablePositionableLabel.cs │ ├── ColorablePositionableLabel.cs │ ├── KeyBind.cs │ ├── ColorARGB.cs │ └── PositionableLabel.cs ├── Patches │ ├── Attributes │ │ ├── HarmonyCHPrefix.cs │ │ ├── HarmonyCHPostfix.cs │ │ └── HarmonyCHPatch.cs │ └── PatchBase.cs ├── Wrappers │ ├── Attributes │ │ ├── WrapperField.cs │ │ ├── WrapperEnum.cs │ │ ├── WrapperProperty.cs │ │ ├── WrapperConstructor.cs │ │ └── WrapperMethod.cs │ ├── Enums.cs │ ├── SoundEffectsManagerWrapper.cs │ ├── CountdownWrapper.cs │ ├── BarrelRollWrapper.cs │ ├── ComboColorWrapper.cs │ ├── CameraShakeWrapper.cs │ ├── HighwayScrollWrapper.cs │ ├── SpNeckRendererWrapper.cs │ ├── BaseNoteRendererWrapper.cs │ ├── BaseNeckControllerWrapper.cs │ ├── PracticeUIWrapper.cs │ ├── FrameRateWrapper.cs │ ├── WrapperBase.cs │ ├── StarPowerWrapper.cs │ ├── SongEntryPropertyWrapper.cs │ ├── PauseMenuWrapper.cs │ ├── BaseGuitarPlayerWrapper.cs │ ├── SongSelectWrapper.cs │ ├── ConfirmationMenuWrapper.cs │ ├── SongEntryWrapper.cs │ ├── MoonNoteWrapper.cs │ ├── SPBarWrapper.cs │ ├── BassAudioManagerWrapper.cs │ ├── CHPlayerWrapper.cs │ ├── SoloCounterWrapper.cs │ ├── SongDirectoryWrapper.cs │ ├── ScoreManagerWrapper.cs │ ├── FadeBehaviourWrapper.cs │ ├── EndOfSongWrapper.cs │ ├── MoonChartWrapper.cs │ ├── MainMenuWrapper.cs │ ├── INIParserWrapper.cs │ ├── SongWrapper.cs │ ├── GameManagerWrapper.cs │ ├── StarProgressWrapper.cs │ └── PlayerProfileWrapper.cs ├── Properties │ └── AssemblyInfo.cs ├── BiendeoCHLib.cs ├── README.md └── VersionCheck.cs ├── LegacyModLoader ├── LoaderInfo.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md └── LegacyModLoader.csproj ├── GigChallenges ├── Challenges │ ├── ChallengeFactory.cs │ └── ScoreChallenge.cs ├── Interfaces │ ├── IChallengeFactory.cs │ └── IChallenge.cs ├── Properties │ └── AssemblyInfo.cs ├── ChallengeBar.cs ├── GigChallenges.csproj └── GigChallenges.cs ├── AccuracyIndicator ├── Components │ └── DestroyOnSceneChange.cs ├── Properties │ └── AssemblyInfo.cs ├── README.md └── AccuracyIndicator.csproj ├── ExtraSongUI ├── Settings │ └── SongUILabel.cs ├── Properties │ └── AssemblyInfo.cs └── ExtraSongUI.csproj ├── PerfectMode ├── Properties │ └── AssemblyInfo.cs ├── README.md └── PerfectMode.csproj ├── ComboIndicator ├── Properties │ └── AssemblyInfo.cs ├── README.md ├── DancingText.cs ├── Settings │ └── Config.cs └── ComboIndicator.csproj ├── SplashTextEditor ├── Properties │ └── AssemblyInfo.cs ├── SplashTextEditor.csproj ├── README.md └── Settings │ └── Config.cs ├── .gitattributes ├── appveyor.yml └── README.md /versions.txt: -------------------------------------------------------------------------------- 1 | v.23.2.2=1.5.2 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: "https://paypal.me/Biendeo" 4 | -------------------------------------------------------------------------------- /Tools/BiendeoCHLibValidator/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS0649: Field 'StarProgressWrapper.transform2Field' is never assigned to, and will always have its default value null 4 | dotnet_diagnostic.CS0649.severity = silent 5 | -------------------------------------------------------------------------------- /Tools/BiendeoCHLibValidator/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /BiendeoCHLib/Settings/IGUIConfigurable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BiendeoCHLib.Settings { 6 | public interface IGUIConfigurable { 7 | void ConfigureGUI(GUIConfigurationStyles styles); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tools/BiendeoCHLibValidator/IValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BiendeoCHLibValidator { 8 | interface IValidator { 9 | bool AssertWorkingDirectory(); 10 | bool Validate(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BiendeoCHLib/Patches/Attributes/HarmonyCHPrefix.cs: -------------------------------------------------------------------------------- 1 | using BepInEx.Logging; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace BiendeoCHLib.Patches.Attributes { 11 | public class HarmonyCHPrefix : Attribute { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BiendeoCHLib/Patches/Attributes/HarmonyCHPostfix.cs: -------------------------------------------------------------------------------- 1 | using BepInEx.Logging; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace BiendeoCHLib.Patches.Attributes { 11 | public class HarmonyCHPostfix : Attribute { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/Attributes/WrapperField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text; 5 | 6 | namespace BiendeoCHLib.Wrappers.Attributes { 7 | public sealed class WrapperField : Attribute { 8 | public readonly string ObfuscatedName; 9 | 10 | public WrapperField(string obfuscatedName) { 11 | ObfuscatedName = obfuscatedName; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/Attributes/WrapperEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BiendeoCHLib.Wrappers.Attributes { 8 | public class WrapperEnum : Attribute { 9 | public readonly string ObfuscatedName; 10 | 11 | public WrapperEnum(string obfuscatedName) { 12 | ObfuscatedName = obfuscatedName; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LegacyModLoader/LoaderInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace LegacyModLoader { 9 | public class LoaderInfo { 10 | public Assembly Assembly; 11 | public Type LoaderType; 12 | public MethodInfo LoadTweakMethod; 13 | public MethodInfo UnloadTweakMethod; 14 | public object Instance; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/Attributes/WrapperProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text; 5 | 6 | namespace BiendeoCHLib.Wrappers.Attributes { 7 | public sealed class WrapperProperty : Attribute { 8 | public readonly string ObfuscatedName; 9 | public readonly BindingFlags BindingFlags; 10 | 11 | public WrapperProperty(string obfuscatedName) { 12 | ObfuscatedName = obfuscatedName; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/Attributes/WrapperConstructor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text; 5 | 6 | namespace BiendeoCHLib.Wrappers.Attributes { 7 | public sealed class WrapperConstructor : Attribute { 8 | public readonly Type[] Types; 9 | 10 | public WrapperConstructor(Type[] types) { 11 | Types = types; 12 | } 13 | 14 | public WrapperConstructor() { 15 | Types = Array.Empty(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /GigChallenges/Challenges/ChallengeFactory.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers; 2 | using GigChallenges.Interfaces; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace GigChallenges.Challenges { 10 | public class ChallengeFactory : IChallengeFactory { 11 | public IChallenge CreateChallenge(GameManagerWrapper gameManager) { 12 | return new ScoreChallenge(gameManager); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /GigChallenges/Interfaces/IChallengeFactory.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace GigChallenges.Interfaces { 9 | interface IChallengeFactory { 10 | /// 11 | /// Creates a challenge given the game state. This should be run once after the game enters the Gameplay scene. 12 | /// 13 | /// 14 | /// 15 | IChallenge CreateChallenge(GameManagerWrapper gameManager); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /BiendeoCHLib/Settings/GUIConfigurationStyles.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | 6 | namespace BiendeoCHLib.Settings { 7 | public class GUIConfigurationStyles { 8 | public GUIStyle LargeLabel; 9 | public GUIStyle SmallLabel; 10 | public GUIStyle Window; 11 | public GUIStyle Toggle; 12 | public GUIStyle Button; 13 | public GUIStyle TextArea; 14 | public GUIStyle TextField; 15 | public GUIStyle Label; 16 | public GUIStyle Box; 17 | public GUIStyle HorizontalSlider; 18 | public GUIStyle HorizontalSliderThumb; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BiendeoCHLib/Settings/FormattableColorablePositionableLabel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Xml.Serialization; 5 | using UnityEngine; 6 | 7 | namespace BiendeoCHLib.Settings { 8 | [Serializable] 9 | public class FormattableColorablePositionableLabel : ColorablePositionableLabel { 10 | public string Format; 11 | 12 | public override void ConfigureGUI(GUIConfigurationStyles styles) { 13 | GUILayout.Label("Format:", styles.SmallLabel); 14 | Format = GUILayout.TextField(Format, styles.TextField); 15 | base.ConfigureGUI(styles); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE] A brief description of the new feature" 5 | labels: enhancement, new tweak suggestion 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /AccuracyIndicator/Components/DestroyOnSceneChange.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using UnityEngine; 7 | using UnityEngine.SceneManagement; 8 | 9 | namespace AccuracyIndicator.Components { 10 | public class DestroyOnSceneChange : MonoBehaviour { 11 | private bool sceneChanged; 12 | 13 | void Start() { 14 | SceneManager.activeSceneChanged += delegate (Scene _, Scene __) { 15 | sceneChanged = true; 16 | }; 17 | } 18 | 19 | void Update() { 20 | if (sceneChanged) { 21 | Destroy(gameObject); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tools/BiendeoCHLibValidator/WrapperValidator.cs: -------------------------------------------------------------------------------- 1 | using BepInEx.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BiendeoCHLibValidator { 9 | /// 10 | /// Validates the wrapper bound fields. 11 | /// 12 | class WrapperValidator : IValidator { 13 | private ManualLogSource logger; 14 | private bool valid; 15 | 16 | public WrapperValidator(ManualLogSource logger) { 17 | this.logger = logger; 18 | valid = true; 19 | } 20 | 21 | public bool AssertWorkingDirectory() { 22 | return true; 23 | } 24 | 25 | public bool Validate() { 26 | return valid; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/Enums.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BiendeoCHLib.Wrappers { 9 | [WrapperEnum("\u0312\u0318\u0312\u031C\u030E\u0316\u0315\u030F\u0314\u0314\u031B")] 10 | public enum Difficulty : sbyte { 11 | Easy, 12 | Medium, 13 | Hard, 14 | Expert 15 | } 16 | 17 | [WrapperEnum("\u0311\u0318\u0315\u030D\u0312\u031B\u0313\u030D\u0311\u030F\u0311")] 18 | public enum InstrumentType : sbyte { 19 | None = -1, 20 | Guitar, 21 | Bass, 22 | Rhythm, 23 | GuitarCoop, 24 | GHLGuitar, 25 | GHLBass, 26 | Drums, 27 | Keys, 28 | Band 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ExtraSongUI/Settings/SongUILabel.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Settings; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Xml.Serialization; 8 | using UnityEngine; 9 | 10 | namespace ExtraSongUI.Settings { 11 | public class SongUILabel : FormattableColorablePositionableLabel { 12 | [XmlIgnore] 13 | public int WindowId = UnityEngine.Random.Range(int.MinValue, int.MaxValue); 14 | public string Name; 15 | 16 | public override void ConfigureGUI(GUIConfigurationStyles styles) { 17 | GUILayout.Label("Name:", styles.SmallLabel); 18 | Name = GUILayout.TextField(Name, styles.TextField); 19 | base.ConfigureGUI(styles); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/SoundEffectsManagerWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BiendeoCHLib.Wrappers { 10 | [Wrapper("\u031C\u0314\u030D\u0313\u0311\u0312\u0311\u0319\u0314\u031A\u030F")] 11 | public struct SoundEffectsManagerWrapper { 12 | 13 | #region Fields 14 | 15 | public static int SomeBool { 16 | get => (int)someBoolField.GetValue(null); 17 | set => someBoolField.SetValue(null, value); 18 | } 19 | [WrapperField("\u030D\u031C\u0313\u030D\u0313\u031B\u0310\u030F\u031A\u0311\u0313")] 20 | private static readonly FieldInfo someBoolField; 21 | 22 | #endregion 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BiendeoCHLib/Patches/PatchBase.cs: -------------------------------------------------------------------------------- 1 | using BepInEx.Logging; 2 | using BiendeoCHLib.Patches.Attributes; 3 | using HarmonyLib; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace BiendeoCHLib.Patches { 13 | public abstract class PatchBase { 14 | public static void InitializePatches(Harmony harmony, Assembly assembly, ManualLogSource logger) { 15 | foreach (var type in assembly.GetTypes()) { 16 | var patch = type.GetCustomAttribute(); 17 | if (patch != null) { 18 | logger.LogDebug($"Initialising patches with class {type.Name}"); 19 | patch.InitializePatch(harmony, type, logger); 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /BiendeoCHLib/Settings/ColorablePositionableLabel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Xml.Serialization; 5 | using UnityEngine; 6 | 7 | namespace BiendeoCHLib.Settings { 8 | [Serializable] 9 | public class ColorablePositionableLabel : PositionableLabel { 10 | public ColorARGB Color; 11 | 12 | public override void ConfigureGUI(GUIConfigurationStyles styles) { 13 | base.ConfigureGUI(styles); 14 | Color.ConfigureGUI(styles); 15 | } 16 | 17 | public override GUIStyle Style => new GUIStyle { 18 | fontSize = Size, 19 | alignment = Alignment, 20 | fontStyle = (Bold ? FontStyle.Bold : FontStyle.Normal) | (Italic ? FontStyle.Italic : FontStyle.Normal), 21 | normal = new GUIStyleState { 22 | textColor = Color.Color 23 | } 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/CountdownWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using System.Security.Cryptography; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using UnityEngine; 13 | 14 | namespace BiendeoCHLib.Wrappers { 15 | [Wrapper(typeof(Countdown))] 16 | public struct CountdownWrapper { 17 | public Countdown Countdown { get; private set; } 18 | 19 | public static CountdownWrapper Wrap(Countdown countdown) => new CountdownWrapper { 20 | Countdown = countdown 21 | }; 22 | 23 | public override bool Equals(object obj) => Countdown.Equals(obj); 24 | 25 | public override int GetHashCode() => Countdown.GetHashCode(); 26 | 27 | public bool IsNull() => Countdown == null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/BarrelRollWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using System.Security.Cryptography; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using UnityEngine; 13 | 14 | namespace BiendeoCHLib.Wrappers { 15 | [Wrapper(typeof(BarrelRoll))] 16 | public struct BarrelRollWrapper { 17 | public BarrelRoll BarrelRoll { get; private set; } 18 | 19 | public static BarrelRollWrapper Wrap(BarrelRoll barrelRoll) => new BarrelRollWrapper { 20 | BarrelRoll = barrelRoll 21 | }; 22 | 23 | public override bool Equals(object obj) => BarrelRoll.Equals(obj); 24 | 25 | public override int GetHashCode() => BarrelRoll.GetHashCode(); 26 | 27 | public bool IsNull() => BarrelRoll == null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/ComboColorWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using System.Security.Cryptography; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using UnityEngine; 13 | 14 | namespace BiendeoCHLib.Wrappers { 15 | [Wrapper(typeof(ComboColor))] 16 | public struct ComboColorWrapper { 17 | public ComboColor ComboColor { get; private set; } 18 | 19 | public static ComboColorWrapper Wrap(ComboColor comboColor) => new ComboColorWrapper { 20 | ComboColor = comboColor 21 | }; 22 | 23 | public override bool Equals(object obj) => ComboColor.Equals(obj); 24 | 25 | public override int GetHashCode() => ComboColor.GetHashCode(); 26 | 27 | public bool IsNull() => ComboColor == null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/CameraShakeWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using System.Security.Cryptography; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using UnityEngine; 13 | 14 | namespace BiendeoCHLib.Wrappers { 15 | [Wrapper(typeof(CameraShake))] 16 | public struct CameraShakeWrapper { 17 | public CameraShake CameraShake { get; private set; } 18 | 19 | public static CameraShakeWrapper Wrap(CameraShake cameraShake) => new CameraShakeWrapper { 20 | CameraShake = cameraShake 21 | }; 22 | 23 | public override bool Equals(object obj) => CameraShake.Equals(obj); 24 | 25 | public override int GetHashCode() => CameraShake.GetHashCode(); 26 | 27 | public bool IsNull() => CameraShake == null; 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/HighwayScrollWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using System.Security.Cryptography; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using UnityEngine; 13 | 14 | namespace BiendeoCHLib.Wrappers { 15 | [Wrapper(typeof(HighwayScroll))] 16 | public struct HighwayScrollWrapper { 17 | public HighwayScroll HighwayScroll { get; private set; } 18 | 19 | public static HighwayScrollWrapper Wrap(HighwayScroll highwayScroll) => new HighwayScrollWrapper { 20 | HighwayScroll = highwayScroll 21 | }; 22 | 23 | public override bool Equals(object obj) => HighwayScroll.Equals(obj); 24 | 25 | public override int GetHashCode() => HighwayScroll.GetHashCode(); 26 | 27 | public bool IsNull() => HighwayScroll == null; 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/SpNeckRendererWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using System.Security.Cryptography; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using UnityEngine; 13 | 14 | namespace BiendeoCHLib.Wrappers { 15 | [Wrapper(typeof(SpNeckRenderer))] 16 | public struct SpNeckRendererWrapper { 17 | public SpNeckRenderer SpNeckRenderer { get; private set; } 18 | 19 | public static SpNeckRendererWrapper Wrap(SpNeckRenderer spNeckRenderer) => new SpNeckRendererWrapper { 20 | SpNeckRenderer = spNeckRenderer 21 | }; 22 | 23 | public override bool Equals(object obj) => SpNeckRenderer.Equals(obj); 24 | 25 | public override int GetHashCode() => SpNeckRenderer.GetHashCode(); 26 | 27 | public bool IsNull() => SpNeckRenderer == null; 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/BaseNoteRendererWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using System.Security.Cryptography; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using UnityEngine; 13 | 14 | namespace BiendeoCHLib.Wrappers { 15 | [Wrapper(typeof(BaseNoteRenderer))] 16 | public struct BaseNoteRendererWrapper { 17 | public BaseNoteRenderer BaseNoteRenderer { get; private set; } 18 | 19 | public static BaseNoteRendererWrapper Wrap(BaseNoteRenderer baseNoteRenderer) => new BaseNoteRendererWrapper { 20 | BaseNoteRenderer = baseNoteRenderer 21 | }; 22 | 23 | public override bool Equals(object obj) => BaseNoteRenderer.Equals(obj); 24 | 25 | public override int GetHashCode() => BaseNoteRenderer.GetHashCode(); 26 | 27 | public bool IsNull() => BaseNoteRenderer == null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/BaseNeckControllerWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using System.Security.Cryptography; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using UnityEngine; 13 | 14 | namespace BiendeoCHLib.Wrappers { 15 | [Wrapper(typeof(BaseNeckController))] 16 | public struct BaseNeckControllerWrapper { 17 | public BaseNeckController BaseNeckController { get; private set; } 18 | 19 | public static BaseNeckControllerWrapper Wrap(BaseNeckController baseNeckController) => new BaseNeckControllerWrapper { 20 | BaseNeckController = baseNeckController 21 | }; 22 | 23 | public override bool Equals(object obj) => BaseNeckController.Equals(obj); 24 | 25 | public override int GetHashCode() => BaseNeckController.GetHashCode(); 26 | 27 | public bool IsNull() => BaseNeckController == null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/Attributes/WrapperMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text; 5 | 6 | namespace BiendeoCHLib.Wrappers.Attributes { 7 | public sealed class WrapperMethod : Attribute { 8 | public readonly string ObfuscatedName; 9 | public readonly Type[] Types; 10 | 11 | public WrapperMethod(string obfuscatedName) { 12 | ObfuscatedName = obfuscatedName; 13 | Types = Array.Empty(); 14 | } 15 | 16 | public WrapperMethod(string obfuscatedName, Type[] types) { 17 | ObfuscatedName = obfuscatedName; 18 | Types = types; 19 | } 20 | 21 | public MethodInfo GetMethodInfo(Type type) { 22 | if (Types.Length == 0) { 23 | return type.GetMethod(ObfuscatedName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 24 | } else { 25 | return type.GetMethod(ObfuscatedName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Types, Array.Empty()); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG] A brief description of your bug" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Clone Hero Version [e.g. v0.23.2.2] 29 | - Using the new Clone Hero Launcher [yes or no] 30 | - Tweak version [e.g. v1.1.0.0] 31 | - You can find the tweak version by right clicking on the tweak's DLL, clicking *Properties*, then *Details*, and you should see it under *Product version*. 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/PracticeUIWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace BiendeoCHLib.Wrappers { 11 | [Wrapper(typeof(PracticeUI))] 12 | public struct PracticeUIWrapper { 13 | public PracticeUI PracticeUI { get; private set; } 14 | 15 | public static PracticeUIWrapper Wrap(PracticeUI practiceUI) => new PracticeUIWrapper { 16 | PracticeUI = practiceUI 17 | }; 18 | 19 | public override bool Equals(object obj) => PracticeUI.Equals(obj); 20 | 21 | public override int GetHashCode() => PracticeUI.GetHashCode(); 22 | 23 | public bool IsNull() => PracticeUI == null; 24 | 25 | #region Fields 26 | 27 | public float SomeFloat { 28 | get => someFloatField(PracticeUI); 29 | set => someFloatField(PracticeUI) = value; 30 | } 31 | [WrapperField("\u030E\u0316\u030F\u0314\u030E\u0312\u0315\u0317\u0315\u031B\u0316")] 32 | private static readonly AccessTools.FieldRef someFloatField; 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/FrameRateWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using TMPro; 10 | 11 | namespace BiendeoCHLib.Wrappers { 12 | [Wrapper(typeof(FrameRate))] 13 | public struct FrameRateWrapper { 14 | public FrameRate FrameRate { get; private set; } 15 | 16 | public static FrameRateWrapper Wrap(FrameRate frameRate) => new FrameRateWrapper { 17 | FrameRate = frameRate 18 | }; 19 | 20 | public override bool Equals(object obj) => FrameRate.Equals(obj); 21 | 22 | public override int GetHashCode() => FrameRate.GetHashCode(); 23 | 24 | public bool IsNull() => FrameRate == null; 25 | 26 | #region Fields 27 | 28 | public TextMeshProUGUI Text { 29 | get => textField(FrameRate); 30 | set => textField(FrameRate) = value; 31 | } 32 | [WrapperField("\u0318\u030F\u0319\u0310\u0312\u0310\u030E\u0314\u0310\u031A\u0313")] 33 | private static readonly AccessTools.FieldRef textField; 34 | 35 | #endregion 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/WrapperBase.cs: -------------------------------------------------------------------------------- 1 | using BepInEx.Logging; 2 | using BiendeoCHLib.Wrappers.Attributes; 3 | using HarmonyLib; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using UnityEngine; 11 | 12 | namespace BiendeoCHLib.Wrappers { 13 | public abstract class WrapperBase { 14 | private static bool initializedWrappers = false; 15 | 16 | public static void InitializeWrappers(ManualLogSource logger) { 17 | if (!initializedWrappers) { 18 | foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) { 19 | var wrapper = type.GetCustomAttribute(); 20 | if (wrapper != null) { 21 | logger.LogDebug($"Initialising wrapper {type.Name}"); 22 | wrapper.InitializeSingletons(type, logger); 23 | } 24 | } 25 | initializedWrappers = true; 26 | } 27 | } 28 | } 29 | 30 | public static class StringExtensions { 31 | public static string DecodeUnicode(this string s) => string.Join(string.Empty, s.Select(c => (c >= '\u0200' ? $"\\u{(int)c:X4}" : $"{c}"))); 32 | public static bool HasObfuscation(this string s) => s.DecodeUnicode() != s; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/StarPowerWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace BiendeoCHLib.Wrappers { 11 | //TODO: This inherits ChartObject so when that's done, make sure this inherits that. 12 | [Wrapper("\u0311\u0311\u0314\u0317\u0319\u0316\u0312\u030F\u0311\u0315\u0312")] 13 | public struct StarPowerWrapper { 14 | public object StarPower { get; private set; } 15 | 16 | public static StarPowerWrapper Wrap(object starPower) => new StarPowerWrapper { 17 | StarPower = starPower 18 | }; 19 | 20 | public override bool Equals(object obj) => StarPower.Equals(obj); 21 | 22 | public override int GetHashCode() => StarPower.GetHashCode(); 23 | 24 | public bool IsNull() => StarPower == null; 25 | 26 | #region Fields 27 | 28 | public uint Length { 29 | get => lengthField(StarPower); 30 | set => lengthField(StarPower) = value; 31 | } 32 | [WrapperField("\u031C\u031C\u0312\u0319\u0314\u0312\u0317\u031C\u031C\u031C\u031A")] 33 | private static readonly AccessTools.FieldRef lengthField; 34 | 35 | #endregion 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/SongEntryPropertyWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using UnityEngine; 9 | 10 | namespace BiendeoCHLib.Wrappers { 11 | [Wrapper("\u0310\u030F\u030F\u0311\u0319\u0317\u0317\u0313\u031B\u0311\u0312")] 12 | public struct SongEntryPropertyWrapper { 13 | public object SongEntryProperty { get; private set; } 14 | 15 | public static SongEntryPropertyWrapper Wrap(object songEntryProperty) => new SongEntryPropertyWrapper { 16 | SongEntryProperty = songEntryProperty 17 | }; 18 | 19 | public override bool Equals(object obj) => SongEntryProperty.Equals(obj); 20 | 21 | public override int GetHashCode() => SongEntryProperty.GetHashCode(); 22 | 23 | public bool IsNull() => SongEntryProperty == null; 24 | 25 | #region Fields 26 | 27 | #endregion 28 | 29 | #region Properties 30 | 31 | public string ValueLowerCase => (string)valueLowerCaseProperty.GetValue(SongEntryProperty); 32 | [WrapperProperty("\u031A\u030D\u0319\u030F\u031A\u0310\u030F\u031B\u0316\u031A\u0319")] 33 | private static readonly PropertyInfo valueLowerCaseProperty; 34 | 35 | #endregion 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/PauseMenuWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace BiendeoCHLib.Wrappers { 11 | [Wrapper(typeof(PauseMenu))] 12 | public struct PauseMenuWrapper { 13 | public PauseMenu PauseMenu { get; private set; } 14 | 15 | public static PauseMenuWrapper Wrap(PauseMenu pauseMenu) => new PauseMenuWrapper { 16 | PauseMenu = pauseMenu 17 | }; 18 | 19 | public override bool Equals(object obj) => PauseMenu.Equals(obj); 20 | 21 | public override int GetHashCode() => PauseMenu.GetHashCode(); 22 | 23 | public bool IsNull() => PauseMenu == null; 24 | 25 | #region Methods 26 | 27 | public void RestartInPracticeMode() => restartInPracticeModeMethod(PauseMenu); 28 | [WrapperMethod("\u0318\u031A\u0317\u030D\u031B\u031B\u0318\u0313\u030D\u030F\u0314")] 29 | private static readonly FastInvokeHandler restartInPracticeModeMethod; 30 | 31 | public void RestartSong() => restartSongMethod(PauseMenu); 32 | [WrapperMethod("\u030E\u0315\u0315\u0310\u0312\u030E\u0312\u031A\u0313\u0314\u0310")] 33 | private static readonly FastInvokeHandler restartSongMethod; 34 | 35 | #endregion 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/BaseGuitarPlayerWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using UnityEngine; 11 | 12 | namespace BiendeoCHLib.Wrappers { 13 | [Wrapper(typeof(BaseGuitarPlayer))] 14 | public struct BaseGuitarPlayerWrapper { 15 | public BaseGuitarPlayer BaseGuitarPlayer { get; private set; } 16 | 17 | public static BaseGuitarPlayerWrapper Wrap(BaseGuitarPlayer baseGuitarPlayer) => new BaseGuitarPlayerWrapper { 18 | BaseGuitarPlayer = baseGuitarPlayer 19 | }; 20 | 21 | public override bool Equals(object obj) => BaseGuitarPlayer.Equals(obj); 22 | 23 | public override int GetHashCode() => BaseGuitarPlayer.GetHashCode(); 24 | 25 | public bool IsNull() => BaseGuitarPlayer == null; 26 | 27 | #region Casts 28 | 29 | public BasePlayerWrapper CastToBasePlayer() => BasePlayerWrapper.Wrap(BaseGuitarPlayer); 30 | 31 | #endregion 32 | 33 | #region Methods 34 | 35 | public void HitNote(NoteWrapper hitNote) => hitNoteMethod.Invoke(BaseGuitarPlayer, new object[] { hitNote.Note }); 36 | [WrapperMethod("\u0314\u0313\u031C\u0315\u0316\u0318\u0318\u031C\u030D\u0319\u0315")] 37 | private static readonly FastInvokeHandler hitNoteMethod; 38 | 39 | #endregion 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/SongSelectWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using UnityEngine; 10 | 11 | namespace BiendeoCHLib.Wrappers { 12 | [Wrapper(typeof(SongSelect))] 13 | public struct SongSelectWrapper { 14 | public SongSelect SongSelect { get; private set; } 15 | 16 | public static SongSelectWrapper Wrap(SongSelect songSelect) => new SongSelectWrapper { 17 | SongSelect = songSelect 18 | }; 19 | 20 | public override bool Equals(object obj) => SongSelect.Equals(obj); 21 | 22 | public override int GetHashCode() => SongSelect.GetHashCode(); 23 | 24 | public bool IsNull() => SongSelect == null; 25 | 26 | #region Constructors 27 | 28 | public static SongSelectWrapper Construct() => new SongSelectWrapper { 29 | SongSelect = (SongSelect)defaultConstructor.Invoke(Array.Empty()) 30 | }; 31 | [WrapperConstructor] 32 | private static readonly ConstructorInfo defaultConstructor; 33 | 34 | #endregion 35 | 36 | #region Fields 37 | 38 | #endregion 39 | 40 | #region Methods 41 | 42 | public void ResetIndex() => resetIndexMethod(SongSelect); 43 | [WrapperMethod("\u0311\u031A\u0318\u0317\u0318\u0314\u031A\u030D\u0313\u030E\u031A")] 44 | private static readonly FastInvokeHandler resetIndexMethod; 45 | 46 | #endregion 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /BiendeoCHLib/Settings/KeyBind.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Xml.Serialization; 5 | using UnityEngine; 6 | 7 | namespace BiendeoCHLib.Settings { 8 | [Serializable] 9 | public class KeyBind : IGUIConfigurable { 10 | public KeyCode Key; 11 | public bool Shift; 12 | public bool Ctrl; 13 | public bool Alt; 14 | 15 | [XmlIgnore] 16 | private bool WaitingForKey; 17 | [XmlIgnore] 18 | public bool JustSet; 19 | 20 | public bool IsPressed => Input.GetKeyDown(Key) && (Shift == (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))) && (Ctrl == (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))) && (Alt == (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt))); 21 | 22 | public void ConfigureGUI(GUIConfigurationStyles styles) { 23 | if (WaitingForKey) { 24 | foreach (KeyCode key in Enum.GetValues(typeof(KeyCode))) { 25 | if (Input.GetKeyDown(key)) { 26 | if (key != KeyCode.Escape) { 27 | Key = key; 28 | JustSet = true; 29 | } 30 | WaitingForKey = false; 31 | break; 32 | } 33 | } 34 | } 35 | WaitingForKey |= GUILayout.Button(WaitingForKey ? "Press key to bind or ESC to cancel" : $"Keybind: {Key.ToString()}", styles.Button); 36 | Shift = GUILayout.Toggle(Shift, "Shift", styles.Toggle); 37 | Ctrl = GUILayout.Toggle(Ctrl, "Ctrl", styles.Toggle); 38 | Alt = GUILayout.Toggle(Alt, "Alt", styles.Toggle); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/ConfirmationMenuWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using System.Security.Cryptography; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using UnityEngine; 13 | 14 | namespace BiendeoCHLib.Wrappers { 15 | [Wrapper(typeof(ConfirmationMenu))] 16 | public struct ConfirmationMenuWrapper { 17 | public ConfirmationMenu ConfirmationMenu { get; private set; } 18 | 19 | public static ConfirmationMenuWrapper Wrap(ConfirmationMenu confirmationMenu) => new ConfirmationMenuWrapper { 20 | ConfirmationMenu = confirmationMenu 21 | }; 22 | 23 | public override bool Equals(object obj) => ConfirmationMenu.Equals(obj); 24 | 25 | public override int GetHashCode() => ConfirmationMenu.GetHashCode(); 26 | 27 | public bool IsNull() => ConfirmationMenu == null; 28 | 29 | #region Methods 30 | 31 | public void Enable(string message, string option1, string option2, MenuDelegate confirm, MenuDelegate decline) => enableMethod(ConfirmationMenu, message, option1, option2, confirm, decline); 32 | [WrapperMethod("\u0319\u0310\u0319\u031C\u030F\u0313\u0313\u0317\u0311\u0315\u031B")] 33 | private static readonly FastInvokeHandler enableMethod; 34 | 35 | #endregion 36 | 37 | #region Delegates 38 | 39 | public delegate void MenuDelegate(); 40 | 41 | #endregion 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/SongEntryWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using UnityEngine; 10 | 11 | namespace BiendeoCHLib.Wrappers { 12 | [Wrapper(typeof(SongEntry))] 13 | public struct SongEntryWrapper { 14 | public SongEntry SongEntry { get; private set; } 15 | 16 | public static SongEntryWrapper Wrap(SongEntry songEntry) => new SongEntryWrapper { 17 | SongEntry = songEntry 18 | }; 19 | 20 | #region Fields 21 | 22 | #endregion 23 | 24 | #region Properties 25 | 26 | public SongEntryPropertyWrapper Artist => SongEntryPropertyWrapper.Wrap(artistProperty.GetValue(SongEntry)); 27 | [WrapperProperty("Artist")] 28 | private static readonly PropertyInfo artistProperty; 29 | 30 | #endregion 31 | 32 | #region Methods 33 | 34 | public bool ReadMetadataForSong() => (bool)readMetadataForSongMethod(SongEntry); 35 | [WrapperMethod("\u0319\u031B\u0312\u0317\u0316\u0319\u0316\u0318\u030F\u0314\u0314")] 36 | private static readonly FastInvokeHandler readMetadataForSongMethod; 37 | 38 | public SongWrapper GetSongObject(bool someBool = false) => SongWrapper.Wrap(getSongObjectMethod(SongEntry, someBool)); 39 | [WrapperMethod("\u0310\u0314\u0316\u0318\u031A\u0317\u0312\u0311\u0315\u0310\u0311")] 40 | private static readonly FastInvokeHandler getSongObjectMethod; 41 | 42 | 43 | #endregion 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/MoonNoteWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BiendeoCHLib.Wrappers { 9 | [Wrapper("\u031C\u0317\u030E\u0310\u030F\u0319\u0316\u030D\u0319\u030D\u0314")] 10 | public struct MoonNoteWrapper { 11 | public object MoonNote { get; private set; } 12 | 13 | public static MoonNoteWrapper Wrap(object moonNote) => new MoonNoteWrapper { 14 | MoonNote = moonNote 15 | }; 16 | 17 | public override bool Equals(object obj) => MoonNote.Equals(obj); 18 | 19 | public override int GetHashCode() => MoonNote.GetHashCode(); 20 | 21 | public bool IsNull() => MoonNote == null; 22 | 23 | #region Enumerations 24 | 25 | [WrapperEnum("\u0317\u0312\u031C\u0314\u030E\u0311\u0319\u0313\u0317\u0317\u0317")] 26 | public enum FretType { 27 | Green, 28 | Red, 29 | Yellow, 30 | Blue, 31 | Orange, 32 | Open, 33 | W1, 34 | W2, 35 | W3, 36 | B1, 37 | B2, 38 | B3 39 | } 40 | 41 | [WrapperEnum("\u031B\u031B\u0316\u030D\u0310\u0318\u0311\u0312\u031B\u0311\u030D")] 42 | public enum NoteType { 43 | Natural, 44 | Strum, 45 | Hopo, 46 | Tap, 47 | None, 48 | Open, 49 | Mirror, 50 | Shuffle 51 | } 52 | 53 | [Flags] 54 | [WrapperEnum("\u0317\u0310\u031B\u030E\u0314\u0313\u0313\u030D\u0311\u0317\u031A")] 55 | public enum Flags { 56 | None = 0, 57 | Forced = 1, 58 | Tap = 2 59 | } 60 | 61 | #endregion 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /AccuracyIndicator/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("AccuracyIndicator")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Biendeo")] 12 | [assembly: AssemblyProduct("AccuracyIndicator")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("90fe6b48-8612-42c6-82cc-d49a606bdcba")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.5.2.0")] 36 | [assembly: AssemblyFileVersion("1.5.2.0")] 37 | -------------------------------------------------------------------------------- /GigChallenges/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("GigChallenges")] 9 | [assembly: AssemblyDescription("Incentivises what you play!")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Biendeo")] 12 | [assembly: AssemblyProduct("GigChallenges")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("6b3da986-f066-4cf2-a1aa-90d2cff14725")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.5.2.0")] 36 | [assembly: AssemblyFileVersion("1.5.2.0")] 37 | -------------------------------------------------------------------------------- /Tools/BiendeoCHLibValidator/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("BiendeoCHLibValidator")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("BiendeoCHLibValidator")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c4d77d40-30e6-44e1-b3a8-33adb8326452")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /BiendeoCHLib/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("BiendeoCHLib")] 9 | [assembly: AssemblyDescription("Common code for all of Biendeo's mods.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Biendeo")] 12 | [assembly: AssemblyProduct("BiendeoCHLib")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9b89d9f0-9d7f-4ab0-863a-c9bdeeec91bc")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.5.2.0")] 36 | [assembly: AssemblyFileVersion("1.5.2.0")] 37 | -------------------------------------------------------------------------------- /ExtraSongUI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ExtraSongUI")] 9 | [assembly: AssemblyDescription("Shows some extra stuff on-screen while you play!")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Biendeo")] 12 | [assembly: AssemblyProduct("ExtraSongUI")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("6ba711cb-ed1d-49e7-b371-d06ccc215e2c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.5.2.0")] 36 | [assembly: AssemblyFileVersion("1.5.2.0")] 37 | -------------------------------------------------------------------------------- /LegacyModLoader/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("LegacyModLoader")] 9 | [assembly: AssemblyDescription("Loads mods intended for CHLoader.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Biendeo")] 12 | [assembly: AssemblyProduct("LegacyModLoader")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("77e79f76-e7bc-4ed8-9165-63df611bc5bb")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.5.2.0")] 36 | [assembly: AssemblyFileVersion("1.5.2.0")] 37 | -------------------------------------------------------------------------------- /PerfectMode/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("PerfectMode")] 9 | [assembly: AssemblyDescription("Automatically restarts your game if you mess an objective")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Biendeo")] 12 | [assembly: AssemblyProduct("PerfectMode")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("1b27bfcc-1305-439e-b304-9f215b75cb16")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.5.2.0")] 36 | [assembly: AssemblyFileVersion("1.5.2.0")] 37 | -------------------------------------------------------------------------------- /ComboIndicator/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ComboIndicator")] 9 | [assembly: AssemblyDescription("Brings back the old Guitar Hero 3 combo indicator.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Biendeo")] 12 | [assembly: AssemblyProduct("ComboIndicator")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("55140311-41d2-477c-9dd5-2cab0c829ef7")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.5.2.0")] 36 | [assembly: AssemblyFileVersion("1.5.2.0")] 37 | -------------------------------------------------------------------------------- /SplashTextEditor/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SplashTextEditor")] 9 | [assembly: AssemblyDescription("Replaces the splash text with whatever you want!")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Biendeo")] 12 | [assembly: AssemblyProduct("SplashTextEditor")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("ba46ac28-f9ca-48cc-8abc-aef87dd06f1d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.5.2.0")] 36 | [assembly: AssemblyFileVersion("1.5.2.0")] 37 | -------------------------------------------------------------------------------- /GigChallenges/Challenges/ScoreChallenge.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers; 2 | using GigChallenges.Interfaces; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using UnityEngine; 9 | 10 | namespace GigChallenges.Challenges { 11 | public class ScoreChallenge : IChallenge { 12 | private GameManagerWrapper gameManager; 13 | private int score; 14 | 15 | private int bronzeScore = 30000; 16 | private int silverScore = 40000; 17 | private int goldScore = 50000; 18 | 19 | public ScoreChallenge(GameManagerWrapper gameManager) { 20 | this.gameManager = gameManager; 21 | score = 0; 22 | } 23 | 24 | public bool IsFeasible => true; 25 | 26 | public string ChallengeName => "Get The Points"; 27 | 28 | public string ChallengeGoal => "Get a score of 50,000!"; 29 | 30 | public float PercentageToBronze => Mathf.Clamp(score * 1.0f / bronzeScore, 0.0f, 1.0f); 31 | 32 | public float PercentageToSilver => Mathf.Clamp(score * 1.0f / silverScore, 0.0f, 1.0f); 33 | 34 | public float PercentageToGold => Mathf.Clamp(score * 1.0f / goldScore, 0.0f, 1.0f); 35 | 36 | public float PercentageFromBronzeToSilver => Mathf.Clamp((score - bronzeScore) * 1.0f / (silverScore - bronzeScore), 0.0f, 1.0f); 37 | 38 | public float PercentageFromSilverToGold => Mathf.Clamp((score - silverScore) * 1.0f / (goldScore - silverScore), 0.0f, 1.0f); 39 | 40 | public float PercentageOfGoldIsBronze => bronzeScore * 1.0f / goldScore; 41 | 42 | public float PercentageOfGoldIsSilver => silverScore * 1.0f / goldScore; 43 | 44 | public void Update() { 45 | score = gameManager.BasePlayers[0].Score; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/SPBarWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using UnityEngine; 10 | 11 | namespace BiendeoCHLib.Wrappers { 12 | [Wrapper(typeof(SPBar))] 13 | public struct SPBarWrapper { 14 | public SPBar SPBar { get; private set; } 15 | 16 | public static SPBarWrapper Wrap(SPBar spBar) => new SPBarWrapper { 17 | SPBar = spBar 18 | }; 19 | 20 | public override bool Equals(object obj) => SPBar.Equals(obj); 21 | 22 | public override int GetHashCode() => SPBar.GetHashCode(); 23 | 24 | public bool IsNull() => SPBar == null; 25 | 26 | #region Casts 27 | 28 | public MonoBehaviour CastToMonoBehaviour() => SPBar; 29 | 30 | #endregion 31 | 32 | #region Fields 33 | 34 | public float LastFillAmount { 35 | get => lastFillAmountField(SPBar); 36 | set => lastFillAmountField(SPBar) = value; 37 | } 38 | [WrapperField("\u0319\u031C\u031A\u030E\u0314\u030E\u0312\u0311\u0317\u0318\u0316")] 39 | private static readonly AccessTools.FieldRef lastFillAmountField; 40 | 41 | #endregion 42 | 43 | #region Methods 44 | 45 | public void Awake() => awakeMethod(SPBar); 46 | [WrapperMethod("Awake")] 47 | private static readonly FastInvokeHandler awakeMethod; 48 | 49 | public void SetFill(float fillAmount, bool isSPActive) => setFillMethod(SPBar, fillAmount, isSPActive); 50 | [WrapperMethod("\u031A\u0312\u030D\u0317\u0312\u031A\u0315\u0311\u0312\u030F\u0315")] 51 | private static readonly FastInvokeHandler setFillMethod; 52 | 53 | #endregion 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /BiendeoCHLib/BiendeoCHLib.cs: -------------------------------------------------------------------------------- 1 | using BepInEx; 2 | using BepInEx.Configuration; 3 | using BiendeoCHLib.Wrappers; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using UnityEngine; 11 | using UnityEngine.SceneManagement; 12 | using UnityEngine.UI; 13 | 14 | namespace BiendeoCHLib { 15 | [BepInPlugin("com.biendeo.biendeochlib", "Biendeo CH Lib", "1.5.2")] 16 | public class BiendeoCHLib : BaseUnityPlugin { 17 | public static BiendeoCHLib Instance { get; private set; } 18 | public string[] SystemFonts { get; private set; } 19 | public const string CloneHeroDefaultFontName = "Clone Hero Default Font"; 20 | public Font CloneHeroDefaultFont { get; private set; } 21 | 22 | private VersionCheck versionCheck; 23 | 24 | public BiendeoCHLib() { 25 | Instance = this; 26 | WrapperBase.InitializeWrappers(Logger); 27 | 28 | CloneHeroDefaultFont = null; 29 | } 30 | 31 | public void Awake() { 32 | versionCheck = gameObject.AddComponent(); 33 | versionCheck.InitializeSettings(Assembly.GetExecutingAssembly(), Config); 34 | 35 | var fonts = Font.GetOSInstalledFontNames().ToList(); 36 | fonts.Add("Clone Hero Default"); 37 | fonts.Sort(); 38 | 39 | SystemFonts = fonts.ToArray(); 40 | } 41 | 42 | public void Update() { 43 | if (CloneHeroDefaultFont == null) { 44 | if (SceneManager.GetActiveScene().name == "Main Menu") { 45 | 46 | //TODO: Get the font directly from the bundle? 47 | 48 | CloneHeroDefaultFont = GameObject.Find("Profile Title").GetComponent().font; 49 | Logger.LogDebug(CloneHeroDefaultFont.name); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /BiendeoCHLib/README.md: -------------------------------------------------------------------------------- 1 | # Biendeo CH Lib 2 | The common code for all of my mods. If you're a developer, you may also use this to help make your own mods! 3 | 4 | ## How to install 5 | - You will need Clone Hero **v0.23.2.2**. You can use any version from the website (i.e. Win64, Win32, Mac, or Linux), but your mileage with the launcher version may vary. 6 | - Install [BepInEx v5.4.17](https://github.com/BepInEx/BepInEx/releases/tag/v5.4.17) into your Clone Hero directory. 7 | - Download the appropriate version and extract **all** of its files into your Clone Hero directory. 8 | - Please verify that BepInEx has initialised by running the game after extracting, and then checking that there are five folders and a `LogOutput.log` file inside the `BepInEx` folder. One of those folders will be named `plugins`, and you'll need that to run the mods. 9 | - Go to the [Releases page](https://github.com/Biendeo/My-Clone-Hero-Tweaks/releases) and download the latest version of Biendeo CH Lib you want for your version of Clone Hero. 10 | - All the downloads are `.zip` files and will need to be extracted to your Clone Hero directory. They should merge with the existing `BepInEx` folder. You can verify it's in the correct place if `BepInEx\plugins\Biendeo CH Lib` exists. 11 | - To ensure that the mods have been extracted properly, check that `LogOutput.log` (or `LogOutput.log.1`, whichever is newer) has this line: `[Info : BepInEx] Loading [Biendeo CH Lib 1.5.2.0]` 12 | 13 | ## How to uninstall 14 | - Delete the folder `BepInEx\plugins\Biendeo CH Lib`. 15 | 16 | ## How to use 17 | ### Default usage 18 | There's nothing a regular user would probably get out of this mod by itself; it's mostly boilerplate for my other mods, so you probably want those as well. -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/BassAudioManagerWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using UnityEngine; 11 | 12 | namespace BiendeoCHLib.Wrappers { 13 | [Wrapper(typeof(BassAudioManager))] 14 | public struct BassAudioManagerWrapper { 15 | public BassAudioManager BassAudioManager { get; private set; } 16 | 17 | public static BassAudioManagerWrapper Wrap(BassAudioManager bassAudioManager) => new BassAudioManagerWrapper { 18 | BassAudioManager = bassAudioManager 19 | }; 20 | 21 | public override bool Equals(object obj) => BassAudioManager.Equals(obj); 22 | 23 | public override int GetHashCode() => BassAudioManager.GetHashCode(); 24 | 25 | public bool IsNull() => BassAudioManager == null; 26 | 27 | #region Fields 28 | 29 | //TODO: Statics are a little iffy with FieldRef, migrate this when it works. 30 | public static BassAudioManagerWrapper Instance { 31 | get => Wrap((BassAudioManager)instanceField.GetValue(null)); 32 | set => instanceField.SetValue(null, value.BassAudioManager); 33 | } 34 | [WrapperField("\u0312\u0313\u0310\u0315\u030E\u0319\u030D\u0318\u0313\u030E\u031A")] 35 | private static readonly FieldInfo instanceField; 36 | 37 | public SongEntryWrapper MenuSong { 38 | get => SongEntryWrapper.Wrap(menuSongField(BassAudioManager)); 39 | set => menuSongField(BassAudioManager) = value.SongEntry; 40 | } 41 | [WrapperField("\u030F\u030F\u031B\u030D\u0316\u0319\u030F\u0314\u0316\u031A\u0316")] 42 | private static readonly AccessTools.FieldRef menuSongField; 43 | 44 | #endregion 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/CHPlayerWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace BiendeoCHLib.Wrappers { 11 | [Wrapper("\u0315\u0313\u0315\u030F\u030E\u030D\u0314\u031C\u0313\u0316\u0313")] 12 | public struct CHPlayerWrapper { 13 | public object CHPlayer { get; private set; } 14 | 15 | public static CHPlayerWrapper Wrap(object chPlayer) => new CHPlayerWrapper { 16 | CHPlayer = chPlayer 17 | }; 18 | 19 | public override bool Equals(object obj) => CHPlayer.Equals(obj); 20 | 21 | public override int GetHashCode() => CHPlayer.GetHashCode(); 22 | 23 | public bool IsNull() => CHPlayer == null; 24 | 25 | #region Fields 26 | 27 | public PlayerProfileWrapper PlayerProfile { 28 | get => PlayerProfileWrapper.Wrap(playerProfileField(CHPlayer)); 29 | set => playerProfileField(CHPlayer) = value.PlayerProfile; 30 | } 31 | [WrapperField("\u0313\u0318\u0314\u0316\u0313\u0317\u0312\u0317\u031A\u0314\u031A")] 32 | private static readonly AccessTools.FieldRef playerProfileField; 33 | 34 | public int PlayerIndex { 35 | get => playerIndexField(CHPlayer); 36 | set => playerIndexField(CHPlayer) = value; 37 | } 38 | [WrapperField("\u0311\u0315\u031C\u031A\u031A\u0314\u030F\u0317\u0312\u0315\u0316")] 39 | private static readonly AccessTools.FieldRef playerIndexField; 40 | 41 | public bool IsModifyingProfile { 42 | get => isModifyingProfileField(CHPlayer); 43 | set => isModifyingProfileField(CHPlayer) = value; 44 | } 45 | [WrapperField("\u030F\u030F\u0316\u0316\u0313\u0311\u0311\u0317\u0319\u0313\u0319")] 46 | private static readonly AccessTools.FieldRef isModifyingProfileField; 47 | 48 | #endregion 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/SoloCounterWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | using System.Text; 7 | 8 | namespace BiendeoCHLib.Wrappers { 9 | [Wrapper(typeof(SoloCounter))] 10 | public struct SoloCounterWrapper { 11 | public SoloCounter SoloCounter { get; private set; } 12 | 13 | public static SoloCounterWrapper Wrap(SoloCounter soloCounter) => new SoloCounterWrapper { 14 | SoloCounter = soloCounter 15 | }; 16 | 17 | public override bool Equals(object obj) => SoloCounter.Equals(obj); 18 | 19 | public override int GetHashCode() => SoloCounter.GetHashCode(); 20 | 21 | public bool IsNull() => SoloCounter == null; 22 | 23 | #region Fields 24 | 25 | public GameManagerWrapper GameManager { 26 | get => GameManagerWrapper.Wrap(gameManagerField(SoloCounter)); 27 | set => gameManagerField(SoloCounter) = value.GameManager; 28 | } 29 | [WrapperField("\u030D\u0317\u0319\u031A\u031A\u030D\u0316\u030D\u0310\u030E\u0313")] 30 | private static readonly AccessTools.FieldRef gameManagerField; 31 | 32 | public bool AnimateText { 33 | get => animateTextField(SoloCounter); 34 | set => animateTextField(SoloCounter) = value; 35 | } 36 | [WrapperField("\u030D\u0311\u031B\u0312\u0319\u0317\u0315\u031C\u031B\u0319\u031C")] 37 | private static readonly AccessTools.FieldRef animateTextField; 38 | 39 | public bool Bool2 { 40 | get => bool2Field(SoloCounter); 41 | set => bool2Field(SoloCounter) = value; 42 | } 43 | [WrapperField("\u030F\u0316\u0317\u031C\u0315\u031B\u031A\u0313\u030F\u0311\u0310")] 44 | private static readonly AccessTools.FieldRef bool2Field; 45 | 46 | public bool Bool3 { 47 | get => bool3Field(SoloCounter); 48 | set => bool3Field(SoloCounter) = value; 49 | } 50 | [WrapperField("\u031A\u031A\u030D\u0315\u0317\u0318\u0319\u0317\u0319\u031C\u0317")] 51 | private static readonly AccessTools.FieldRef bool3Field; 52 | 53 | #endregion 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /BiendeoCHLib/Settings/ColorARGB.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | 6 | namespace BiendeoCHLib.Settings { 7 | [Serializable] 8 | public class ColorARGB : IGUIConfigurable { 9 | public uint ARGB; 10 | 11 | private ColorARGB() { 12 | ARGB = 0; 13 | } 14 | 15 | public ColorARGB(uint argb) { 16 | ARGB = argb; 17 | } 18 | 19 | public ColorARGB(Color color) { 20 | ARGB = ColorToARGB(color); 21 | } 22 | 23 | public Color Color => ARGBToColor(ARGB); 24 | 25 | public static uint ColorToARGB(Color color) => (((uint)(color.a * 255.0f) & 0xFF) << 24) | (((uint)(color.r * 255.0f) & 0xFF) << 16) | (((uint)(color.g * 255.0f) & 0xFF) << 8) | ((uint)(color.b * 255.0f) & 0xFF); 26 | 27 | public static Color ARGBToColor(uint color) => new Color(((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f); 28 | 29 | public void ConfigureGUI(GUIConfigurationStyles styles) { 30 | var color = ARGBToColor(ARGB); 31 | GUILayout.Label($"R = {(int)(color.r * 255.0f)}, G = {(int)(color.g * 255.0f)}, B = {(int)(color.b * 255.0f)}, A = {(int)(color.a * 255.0f)}", new GUIStyle(styles.SmallLabel) { 32 | normal = new GUIStyleState { 33 | textColor = new Color(color.r, color.g, color.b), 34 | }, 35 | fontStyle = FontStyle.Bold 36 | }); 37 | GUILayout.Label("Red", styles.SmallLabel); 38 | color.r = GUILayout.HorizontalSlider(color.r, 0.0f, 1.0f, styles.HorizontalSlider, styles.HorizontalSliderThumb); 39 | GUILayout.Label("Green", styles.SmallLabel); 40 | color.g = GUILayout.HorizontalSlider(color.g, 0.0f, 1.0f, styles.HorizontalSlider, styles.HorizontalSliderThumb); 41 | GUILayout.Label("Blue", styles.SmallLabel); 42 | color.b = GUILayout.HorizontalSlider(color.b, 0.0f, 1.0f, styles.HorizontalSlider, styles.HorizontalSliderThumb); 43 | GUILayout.Label("Alpha", styles.SmallLabel); 44 | color.a = GUILayout.HorizontalSlider(color.a, 0.0f, 1.0f, styles.HorizontalSlider, styles.HorizontalSliderThumb); 45 | ARGB = ColorToARGB(color); 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/SongDirectoryWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using UnityEngine; 11 | 12 | namespace BiendeoCHLib.Wrappers { 13 | [Wrapper("\u0310\u0313\u031B\u030F\u0310\u031C\u030D\u0317\u0315\u031A\u0315")] 14 | internal static class SongDirectoryWrapper { 15 | #region Fields 16 | 17 | public static List SetlistSongEntries => ((List)setlistSongEntriesField.GetValue(null)).Select(o => SongEntryWrapper.Wrap(o)).ToList(); 18 | [WrapperField("\u030F\u0315\u0311\u0315\u0313\u0313\u031B\u0318\u0316\u031A\u0317")] 19 | private static readonly FieldInfo setlistSongEntriesField; 20 | 21 | public static List SetlistSongEntries2 => ((List)setlistSongEntriesField2.GetValue(null)).Select(o => SongEntryWrapper.Wrap(o)).ToList(); 22 | [WrapperField("\u0312\u030E\u031B\u030E\u0313\u0316\u0312\u0311\u0317\u0312\u0313")] 23 | private static readonly FieldInfo setlistSongEntriesField2; 24 | 25 | public static List SetlistSongEntries3 => ((List)setlistSongEntriesField3.GetValue(null)).Select(o => SongEntryWrapper.Wrap(o)).ToList(); 26 | [WrapperField("\u031A\u0315\u0314\u031B\u0316\u0318\u0317\u0315\u0315\u030E\u0318")] 27 | private static readonly FieldInfo setlistSongEntriesField3; 28 | 29 | public static int SortCounter { 30 | get => (int)songCounterField.GetValue(null); 31 | set => songCounterField.SetValue(null, value); 32 | } 33 | [WrapperField("\u031A\u0312\u031C\u0319\u0310\u0317\u0319\u0315\u0317\u031A\u031A")] 34 | private static readonly FieldInfo songCounterField; 35 | 36 | #endregion 37 | 38 | #region Methods 39 | 40 | public static void Sort(string forceSort, bool something) => sortMethod.Invoke(null, forceSort, something); 41 | [WrapperMethod("\u0313\u0311\u030F\u0311\u0317\u0312\u0315\u031B\u0313\u031A\u031B")] 42 | private static readonly FastInvokeHandler sortMethod; 43 | 44 | #endregion 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tools/BiendeoCHLibValidator/BiendeoCHLibValidator.cs: -------------------------------------------------------------------------------- 1 | using BepInEx.Logging; 2 | using BiendeoCHLib.Wrappers; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace BiendeoCHLibValidator { 11 | class BiendeoCHLibValidator { 12 | internal static bool LoggingEnabled = true; 13 | 14 | static void Main(string[] args) { 15 | var logger = new ManualLogSource("stdout"); 16 | logger.LogEvent += LogEvent; 17 | LoggingEnabled = false; 18 | 19 | // The current working directory should be the repository root. 20 | WrapperBase.InitializeWrappers(logger); 21 | 22 | LoggingEnabled = true; 23 | 24 | var validators = new IValidator[] { 25 | new StandaloneVsLauncherValidator(logger), 26 | new SourceMapValidator(logger), 27 | new WrapperValidator(logger), 28 | }; 29 | 30 | foreach (var validator in validators) { 31 | if (!validator.AssertWorkingDirectory()) { 32 | Environment.Exit(1); 33 | } 34 | } 35 | foreach (var validator in validators) { 36 | validator.Validate(); 37 | } 38 | } 39 | 40 | private static void LogEvent(object sender, LogEventArgs e) { 41 | if (!LoggingEnabled) return; 42 | ConsoleColor oldBackground = Console.BackgroundColor; 43 | ConsoleColor oldForeground = Console.ForegroundColor; 44 | 45 | switch (e.Level) { 46 | case LogLevel.Debug: 47 | Console.ForegroundColor = ConsoleColor.Gray; 48 | break; 49 | case LogLevel.Info: 50 | Console.ForegroundColor = ConsoleColor.White; 51 | break; 52 | case LogLevel.Warning: 53 | Console.ForegroundColor = ConsoleColor.Yellow; 54 | break; 55 | case LogLevel.Error: 56 | Console.ForegroundColor = ConsoleColor.Red; 57 | break; 58 | case LogLevel.Fatal: 59 | Console.ForegroundColor = ConsoleColor.DarkRed; 60 | break; 61 | default: 62 | Console.ForegroundColor = ConsoleColor.White; 63 | break; 64 | } 65 | 66 | Console.WriteLine($"[{e.Level,-7}] {e.Data}"); 67 | 68 | Console.BackgroundColor = oldBackground; 69 | Console.ForegroundColor = oldForeground; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/ScoreManagerWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using UnityEngine; 10 | 11 | namespace BiendeoCHLib.Wrappers { 12 | [Wrapper(typeof(ScoreManager))] 13 | public struct ScoreManagerWrapper { 14 | public ScoreManager ScoreManager { get; private set; } 15 | 16 | public static ScoreManagerWrapper Wrap(ScoreManager scoreManager) => new ScoreManagerWrapper { 17 | ScoreManager = scoreManager 18 | }; 19 | 20 | public override bool Equals(object obj) => ScoreManager.Equals(obj); 21 | 22 | public override int GetHashCode() => ScoreManager.GetHashCode(); 23 | 24 | public bool IsNull() => ScoreManager == null; 25 | 26 | #region Fields 27 | 28 | public StarProgressWrapper StarProgress { 29 | get => StarProgressWrapper.Wrap(starProgressField(ScoreManager)); 30 | set => starProgressField(ScoreManager) = value.StarProgress; 31 | } 32 | [WrapperField("\u0312\u0318\u0310\u031A\u031C\u031C\u031C\u031B\u030D\u0313\u031B")] 33 | private static readonly AccessTools.FieldRef starProgressField; 34 | 35 | public int UnknownInt1 { 36 | get => unknownInt1Field(ScoreManager); 37 | set => unknownInt1Field(ScoreManager) = value; 38 | } //? Initially 1? 39 | [WrapperField("\u030F\u0317\u031C\u031A\u031A\u031A\u0311\u0317\u030D\u0314\u031C")] 40 | private static readonly AccessTools.FieldRef unknownInt1Field; 41 | 42 | public int OverallCombo { 43 | get => overallComboField(ScoreManager); 44 | set => overallComboField(ScoreManager) = value; 45 | } 46 | [WrapperField("\u0314\u0316\u0314\u0316\u0316\u0319\u0319\u0319\u0315\u0318\u031A")] 47 | private static readonly AccessTools.FieldRef overallComboField; 48 | 49 | public int UnknownInt3 { 50 | get => unknownInt3Field(ScoreManager); 51 | set => unknownInt3Field(ScoreManager) = value; 52 | } 53 | [WrapperField("\u031C\u031A\u031C\u0314\u030E\u031B\u030E\u030E\u0314\u031A\u031C")] 54 | private static readonly AccessTools.FieldRef unknownInt3Field; 55 | 56 | #endregion 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/FadeBehaviourWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Reflection; 7 | using System.Text; 8 | using UnityEngine.UI; 9 | 10 | namespace BiendeoCHLib.Wrappers { 11 | [Wrapper(typeof(FadeBehaviour))] 12 | public struct FadeBehaviourWrapper { 13 | public FadeBehaviour FadeBehaviour { get; private set; } 14 | 15 | public static FadeBehaviourWrapper Wrap(FadeBehaviour fadeBehaviour) => new FadeBehaviourWrapper { 16 | FadeBehaviour = fadeBehaviour 17 | }; 18 | 19 | public override bool Equals(object obj) => FadeBehaviour.Equals(obj); 20 | 21 | public override int GetHashCode() => FadeBehaviour.GetHashCode(); 22 | 23 | public bool IsNull() => FadeBehaviour == null; 24 | 25 | #region Fields 26 | 27 | public static FadeBehaviourWrapper Instance { 28 | get => Wrap((FadeBehaviour)instanceField.GetValue(null)); 29 | set => instanceField.SetValue(null, value.FadeBehaviour); 30 | } 31 | [WrapperField("\u0312\u0313\u0310\u0315\u030E\u0319\u030D\u0318\u0313\u030E\u031A")] 32 | private static readonly FieldInfo instanceField; 33 | 34 | public float TimeToFade { 35 | get => timeToFadeField(FadeBehaviour); 36 | set => timeToFadeField(FadeBehaviour) = value; 37 | } 38 | [WrapperField("\u030F\u031B\u0314\u0311\u0310\u0313\u0314\u0314\u0317\u0318\u0312")] 39 | private static readonly AccessTools.FieldRef timeToFadeField; //? Always 0.4? 40 | 41 | public bool IsFadingOut { 42 | get => isFadingOutField(FadeBehaviour); 43 | set => isFadingOutField(FadeBehaviour) = value; 44 | } 45 | [WrapperField("\u0311\u0310\u031B\u0312\u0315\u030D\u030D\u031B\u0315\u031C\u0314")] 46 | private static readonly AccessTools.FieldRef isFadingOutField; 47 | 48 | public Image FadeGraphic { 49 | get => fadeGraphicField(FadeBehaviour); 50 | set => fadeGraphicField(FadeBehaviour) = value; 51 | } 52 | [WrapperField("fadeGraphic")] 53 | private static readonly AccessTools.FieldRef fadeGraphicField; 54 | 55 | #endregion 56 | 57 | #region Methods 58 | 59 | public IEnumerator InvokeSceneChange(string sceneName) => (IEnumerator)invokeSceneChangeMethod(FadeBehaviour, sceneName); 60 | [WrapperMethod("\u0318\u031C\u0315\u0318\u030E\u031B\u0315\u0310\u030D\u0314\u0314")] 61 | private static readonly FastInvokeHandler invokeSceneChangeMethod; 62 | 63 | #endregion 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/EndOfSongWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using UnityEngine; 11 | 12 | namespace BiendeoCHLib.Wrappers { 13 | [Wrapper(typeof(EndOfSong))] 14 | public struct EndOfSongWrapper { 15 | public EndOfSong EndOfSong { get; private set; } 16 | 17 | public static EndOfSongWrapper Wrap(EndOfSong endOfSong) => new EndOfSongWrapper { 18 | EndOfSong = endOfSong 19 | }; 20 | 21 | public override bool Equals(object obj) => EndOfSong.Equals(obj); 22 | 23 | public override int GetHashCode() => EndOfSong.GetHashCode(); 24 | 25 | public bool IsNull() => EndOfSong == null; 26 | 27 | #region Constructors 28 | 29 | #endregion 30 | 31 | #region Fields 32 | 33 | // Seems to just refer to an object (probably a prefab) called "BotNoSave". 34 | // This is in the Main Canvas where all the text elements are drawn. 35 | // It only has the canvas renderer and the text mesh pro object which indicates either 36 | // if a bot was running, or if a high score was achieved. The component is disabled if neither. 37 | public GameObject BotNoSaveObject { 38 | get => botNoSaveObjectField(EndOfSong); 39 | set => botNoSaveObjectField(EndOfSong) = value; 40 | } 41 | [WrapperField("\u0316\u0316\u030E\u030D\u0316\u0319\u031A\u0312\u030F\u0316\u031A")] 42 | private static readonly AccessTools.FieldRef botNoSaveObjectField; 43 | 44 | // An unused value that starts at 0 and is incremented by Time.deltaTime in Update until it's greater than 1. 45 | // Probably a precursor to the fade behaviour left in code. No other uses. 46 | public float FadeFloat { 47 | get => fadeFloatField(EndOfSong); 48 | set => fadeFloatField(EndOfSong) = value; 49 | } 50 | [WrapperField("\u0312\u031B\u0317\u030D\u031A\u0318\u0315\u030E\u0310\u0312\u0317")] 51 | private static readonly AccessTools.FieldRef fadeFloatField; 52 | 53 | // A true false based on a global variables value. 54 | public bool SomeBool { 55 | get => someBoolField(EndOfSong); 56 | set => someBoolField(EndOfSong) = value; 57 | } 58 | [WrapperField("\u0314\u030F\u0317\u0311\u0310\u0318\u030F\u031A\u0317\u0317\u0317")] 59 | private static readonly AccessTools.FieldRef someBoolField; 60 | 61 | #endregion 62 | 63 | #region Properties 64 | 65 | #endregion 66 | 67 | #region Methods 68 | 69 | #endregion 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /BiendeoCHLib/Settings/PositionableLabel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Xml.Serialization; 5 | using UnityEngine; 6 | 7 | namespace BiendeoCHLib.Settings { 8 | [Serializable] 9 | public class PositionableLabel : IGUIConfigurable { 10 | public int X; 11 | public int Y; 12 | public int Size; 13 | public TextAnchor Alignment; 14 | public bool Bold; 15 | public bool Italic; 16 | public bool Visible; 17 | 18 | [XmlIgnore] 19 | public bool DraggableWindowsEnabled; 20 | 21 | public void DrawLabelWindow(int id) { 22 | if (Visible) { 23 | var r = GUILayout.Window(id, new Rect(X, Y, 20.0f, 30.0f), DraggableWindow, string.Empty); 24 | X = (int)r.x; 25 | Y = (int)r.y; 26 | } 27 | } 28 | 29 | public virtual void ConfigureGUI(GUIConfigurationStyles styles) { 30 | Visible = GUILayout.Toggle(Visible, "Enabled", styles.Toggle); 31 | if (!DraggableWindowsEnabled) { 32 | GUILayout.Label("X", styles.SmallLabel); 33 | X = (int)GUILayout.HorizontalSlider(X, -3840.0f, 3840.0f, styles.HorizontalSlider, styles.HorizontalSliderThumb); 34 | if (int.TryParse(GUILayout.TextField(X.ToString(), styles.TextField), out int x)) X = x; 35 | } else { 36 | GUILayout.Label($"X - {X.ToString()}", styles.SmallLabel); 37 | } 38 | if (!DraggableWindowsEnabled) { 39 | GUILayout.Label("Y", styles.SmallLabel); 40 | Y = (int)GUILayout.HorizontalSlider(Y, -2160, 2160.0f, styles.HorizontalSlider, styles.HorizontalSliderThumb); 41 | if (int.TryParse(GUILayout.TextField(Y.ToString(), styles.TextField), out int y)) Y = y; 42 | } else { 43 | GUILayout.Label($"Y - {Y.ToString()}", styles.SmallLabel); 44 | } 45 | GUILayout.Label("Size", styles.SmallLabel); 46 | Size = (int)GUILayout.HorizontalSlider(Size, 0.0f, 500.0f, styles.HorizontalSlider, styles.HorizontalSliderThumb); 47 | if (int.TryParse(GUILayout.TextField(Size.ToString(), styles.TextField), out int size)) Size = size; 48 | if (GUILayout.Button($"Alignment: {Alignment.ToString()}", styles.Button)) { 49 | Alignment = (TextAnchor)((int)(Alignment + 1) % 9); 50 | } 51 | Bold = GUILayout.Toggle(Bold, "Bold", styles.Toggle); 52 | Italic = GUILayout.Toggle(Italic, "Italic", styles.Toggle); 53 | } 54 | 55 | private void DraggableWindow(int id) { 56 | GUI.DragWindow(); 57 | } 58 | 59 | public Rect Rect => new Rect(X, Y, 0.1f, 0.1f); 60 | 61 | public virtual GUIStyle Style => new GUIStyle { 62 | fontSize = Size, 63 | alignment = Alignment, 64 | fontStyle = (Bold ? FontStyle.Bold : FontStyle.Normal) | (Italic ? FontStyle.Italic : FontStyle.Normal) 65 | }; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/MoonChartWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using UnityEngine; 10 | 11 | namespace BiendeoCHLib.Wrappers { 12 | [Wrapper("\u0311\u0318\u030F\u0314\u0315\u031B\u030F\u0316\u0313\u0316\u0316")] 13 | public struct MoonChartWrapper { 14 | public object MoonChart { get; private set; } 15 | 16 | public static MoonChartWrapper Wrap(object moonChart) => new MoonChartWrapper { 17 | MoonChart = moonChart 18 | }; 19 | 20 | public override bool Equals(object obj) => MoonChart.Equals(obj); 21 | 22 | public override int GetHashCode() => MoonChart.GetHashCode(); 23 | 24 | public bool IsNull() => MoonChart == null; 25 | 26 | #region Fields 27 | 28 | // Old reference had base score and note count in there, maybe those shouldn't be relied on 29 | //? Seems to be the note count but for the wrong difficulty? 30 | public int UnknownInt1 { 31 | get => unknownInt1Field(MoonChart); 32 | set => unknownInt1Field(MoonChart) = value; 33 | } 34 | [WrapperField("\u0314\u0313\u030D\u0311\u0318\u0314\u031C\u0311\u0314\u0310\u0317")] 35 | private static readonly AccessTools.FieldRef unknownInt1Field; 36 | 37 | public int UnknownInt2 { 38 | get => unknownInt2Field(MoonChart); 39 | set => unknownInt2Field(MoonChart) = value; 40 | } 41 | [WrapperField("\u0315\u030F\u0313\u0318\u0318\u0316\u0318\u031B\u030D\u0319\u0313")] 42 | private static readonly AccessTools.FieldRef unknownInt2Field; 43 | 44 | #endregion 45 | 46 | #region Properties 47 | 48 | //? This is the wrong string, so I goofed up somewhere. 49 | //public SongWrapper Song => new SongWrapper(songProperty.GetValue(moonChart)); 50 | //[WrapperField("\u030F\u0315\u031A\u0316\u0318\u031A\u030E\u0319\u0316\u0311\u031A")] 51 | //private static readonly PropertyInfo songProperty; 52 | 53 | // A property for UnknownInt1. 54 | public int UnknownInt1Property => (int)unknownInt1Property.GetValue(MoonChart); 55 | [WrapperProperty("\u0314\u0316\u030F\u0319\u0315\u031B\u0318\u0318\u031B\u0315\u031B")] 56 | private static readonly PropertyInfo unknownInt1Property; 57 | 58 | //? This is not StarPower! 59 | //public StarPowerWrapper[] StarPower => ((object[])starPowerProperty.GetValue(MoonChart)).Select(o => StarPowerWrapper.Wrap(o)).ToArray(); 60 | //[WrapperProperty("\u0314\u0312\u0318\u031C\u031A\u0313\u0314\u0317\u030E\u0315\u031C")] 61 | //private static readonly PropertyInfo starPowerProperty; 62 | 63 | #endregion 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /GigChallenges/Interfaces/IChallenge.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace GigChallenges.Interfaces { 9 | /// 10 | /// Describes a challenge the player can take. Challenges should be given a so that 11 | /// they can observe the game state. 12 | /// 13 | public interface IChallenge { 14 | /// 15 | /// Returns whether this kind of challenge is practical for the given chart. This would rule out things such as 16 | /// charts with no sustains requiring whammy time, or charts with no notes having...anything. 17 | /// 18 | /// 19 | bool IsFeasible { get; } 20 | 21 | /// 22 | /// The name of this challenge (human readable). 23 | /// 24 | /// 25 | string ChallengeName { get; } 26 | 27 | /// 28 | /// A description of this challenge that should indicate to the user what they need to achieve. 29 | /// 30 | /// 31 | string ChallengeGoal { get; } 32 | 33 | /// 34 | /// The percentage from zero to the bronze target [0, 1]. 35 | /// 36 | /// 37 | float PercentageToBronze { get; } 38 | 39 | /// 40 | /// The percentage from zero to the silver target [0, 1]. 41 | /// 42 | /// 43 | float PercentageToSilver { get; } 44 | 45 | /// 46 | /// The percentage from the bronze target to the silver target [0, 1]. 47 | /// 48 | /// 49 | float PercentageFromBronzeToSilver { get; } 50 | 51 | /// 52 | /// The percentage from zero to the gold target [0, 1]. 53 | /// 54 | /// 55 | float PercentageToGold { get; } 56 | 57 | /// 58 | /// The percentage from the silver target to the gold target [0, 1]. 59 | /// 60 | /// 61 | float PercentageFromSilverToGold { get; } 62 | 63 | /// 64 | /// The percentage from zero to the bronze target where gold is 1 [0, 1]. 65 | /// 66 | /// 67 | float PercentageOfGoldIsBronze { get; } 68 | 69 | /// 70 | /// The percentage from zero to the silver target where gold is 1 [0, 1]. 71 | /// 72 | /// 73 | float PercentageOfGoldIsSilver { get; } 74 | 75 | /// 76 | /// Tells the challenge to update itself; useful if performance needs to be watched. 77 | /// 78 | void Update(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /LegacyModLoader/README.md: -------------------------------------------------------------------------------- 1 | # Legacy Mod Loader 2 | An alternative mod loader for CHLoader compatible mods. 3 | 4 | **Please do not use this and CHLoader**, as both will try and instance tweaks, which can cause undefined behaviour. 5 | 6 | ## How to install 7 | - You will need Clone Hero **v0.23.2.2**. You can use any version from the website (i.e. Win64, Win32, Mac, or Linux), but your mileage with the launcher version may vary. 8 | - Install [BepInEx v5.4.17](https://github.com/BepInEx/BepInEx/releases/tag/v5.4.17) into your Clone Hero directory. 9 | - Download the appropriate version and extract **all** of its files into your Clone Hero directory. 10 | - Please verify that BepInEx has initialised by running the game after extracting, and then checking that there are five folders and a `LogOutput.log` file inside the `BepInEx` folder. One of those folders will be named `plugins`, and you'll need that to run the mods. 11 | - Go to the [Releases page](https://github.com/Biendeo/My-Clone-Hero-Tweaks/releases) and download the latest version of Legacy Mod Loader you want for your version of Clone Hero. 12 | - All the downloads are `.zip` files and will need to be extracted to your Clone Hero directory. They should merge with the existing `BepInEx` folder. You can verify it's in the correct place if `BepInEx\plugins\Legacy Mod Loader` exists. 13 | - To ensure that the mods have been extracted properly, check that `LogOutput.log` (or `LogOutput.log.1`, whichever is newer) has this line: `[Info : BepInEx] Loading [Legacy Mod Loader 1.5.2.0]` 14 | 15 | ## How to uninstall 16 | - Delete the folder `BepInEx\plugins\Legacy Mod Loader`. 17 | 18 | ## How to use 19 | ### Default usage 20 | With the tweak loaded for the first time, you'll have a `Tweaks` folder in the your Clone Hero directory. This is where CHLoader mods expect to be installed. Simply extract any legacy *Tweaks* into this folder (i.e. make sure that the mod's `.dll` file is directly in that folder), and Legacy Mod Loader will load it on the next startup of Clone Hero. 21 | 22 | In-game, you can press F1 to bring up a menu showing the names of all the loaded tweaks. If you have the `Enable Unload Feature` enabled in config, you can also click to unload and reload tweaks. This functionality may/may not work depending on the tweak, since CHLoader never properly implemented this functionality. 23 | 24 | **Note:** this is only to be used for *tweaks* and other mods that utilise CHLoader. If a mod says it's for BepInEx, you do not require Legacy Mod Loader and should instead install it in the way described for that mod (for example, in `BepInEx/plugins`). Tweaks are compatible with BepInEx mods though, so you can install both BepInEx mods and CHLoader-style tweaks on the same installation of Clone Hero. -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /LegacyModLoader/LegacyModLoader.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {77E79F76-E7BC-4ED8-9165-63DF611BC5BB} 8 | Library 9 | Properties 10 | LegacyModLoader 11 | LegacyModLoader 12 | v4.8 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | ..\BepInEx-Libraries\BepInEx.dll 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ..\CH-Libraries\UnityEngine.dll 47 | 48 | 49 | ..\CH-Libraries\UnityEngine.CoreModule.dll 50 | 51 | 52 | ..\CH-Libraries\UnityEngine.IMGUIModule.dll 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /BiendeoCHLib/Patches/Attributes/HarmonyCHPatch.cs: -------------------------------------------------------------------------------- 1 | using BepInEx.Logging; 2 | using BiendeoCHLib.Wrappers.Attributes; 3 | using HarmonyLib; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace BiendeoCHLib.Patches.Attributes { 12 | public class HarmonyCHPatch : Attribute { 13 | private readonly Type wrapperType; 14 | private readonly string wrapperMethodName; 15 | 16 | public HarmonyCHPatch(Type wrapperType, string wrapperMethodName) { 17 | this.wrapperType = wrapperType; 18 | this.wrapperMethodName = wrapperMethodName; 19 | } 20 | 21 | public void InitializePatch(Harmony harmony, Type patchType, ManualLogSource logger) { 22 | // Assert that the wrapper type indeed has the methodInfo expected. 23 | MethodInfo targetMethod = null; 24 | var wrapperField = wrapperType.GetFields(BindingFlags.Static | BindingFlags.NonPublic).SingleOrDefault(f => f.Name.ToLower() == $"{wrapperMethodName}Method".ToLower()); 25 | if (wrapperField != default) { 26 | //TODO: Is there a better way than re-invoking the wrapper stuff? 27 | targetMethod = wrapperField.GetCustomAttribute().GetMethodInfo(wrapperType.GetCustomAttribute().WrappedType); 28 | logger.LogDebug($"Found matching method for patch {wrapperType.Name}.{wrapperMethodName}"); 29 | } else { 30 | logger.LogError($"Could not find matching method for patch {wrapperType.Name}.{wrapperMethodName}"); 31 | #if DEBUG 32 | logger.LogError($"Program terminating until this patch is fixed!"); 33 | Environment.Exit(1); 34 | #endif 35 | return; 36 | } 37 | 38 | // Now determine the prefix and postfix methods in the patch type. 39 | MethodInfo prefixMethod = null; 40 | MethodInfo postfixMethod = null; 41 | foreach (var method in patchType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) { 42 | if (method.Name == "Prefix" || method.GetCustomAttribute() != null) { 43 | logger.LogDebug($"Found prefix method {method.Name}"); 44 | #if DEBUG 45 | if (prefixMethod != null) { 46 | logger.LogError($"This replaces a previously defined prefix method, terminating..."); 47 | Environment.Exit(1); 48 | } 49 | #endif 50 | prefixMethod = method; 51 | } 52 | if (method.Name == "Postfix" || method.GetCustomAttribute() != null) { 53 | logger.LogDebug($"Found postfix method {method.Name}"); 54 | #if DEBUG 55 | if (postfixMethod != null) { 56 | logger.LogError($"This replaces a previously defined postfix method, terminating..."); 57 | Environment.Exit(1); 58 | } 59 | #endif 60 | postfixMethod = method; 61 | } 62 | } 63 | if (prefixMethod != null || postfixMethod != null) { 64 | harmony.Patch(targetMethod, prefix: prefixMethod == null ? null : new HarmonyMethod(prefixMethod), postfix: postfixMethod == null ? null : new HarmonyMethod(postfixMethod)); 65 | } else { 66 | logger.LogWarning("Found no prefix/postfix methods for patch, so no action will be done."); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ComboIndicator/README.md: -------------------------------------------------------------------------------- 1 | # Combo Indicator 2 | Get that Guitar Hero 3 nostalgia by having an animated note streak indicator pop up at 50, 100, 200, and so on combo! 3 | 4 | ## Preview 5 | ![Combo Indicator Preview 1 (gfycat)](https://giant.gfycat.com/GreenThickIndianrockpython.gif) 6 | 7 | ## How to install 8 | - You will need Clone Hero **v0.23.2.2**. You can use any version from the website (i.e. Win64, Win32, Mac, or Linux), but your mileage with the launcher version may vary. 9 | - Install [BepInEx v5.4.17](https://github.com/BepInEx/BepInEx/releases/tag/v5.4.17) into your Clone Hero directory. 10 | - Download the appropriate version and extract **all** of its files into your Clone Hero directory. 11 | - Please verify that BepInEx has initialised by running the game after extracting, and then checking that there are five folders and a `LogOutput.log` file inside the `BepInEx` folder. One of those folders will be named `plugins`, and you'll need that to run the mods. 12 | - Go to the [Releases page](https://github.com/Biendeo/My-Clone-Hero-Tweaks/releases) and download the latest version of Combo Indicator you want for your version of Clone Hero. 13 | - All the downloads are `.zip` files and will need to be extracted to your Clone Hero directory. They should merge with the existing `BepInEx` folder. You can verify it's in the correct place if `BepInEx\plugins\Combo Indicator` exists. 14 | - You will also need `Biendeo CH Lib` installed before this mod works! 15 | - To ensure that the mods have been extracted properly, check that `LogOutput.log` (or `LogOutput.log.1`, whichever is newer) has this line: `[Info : BepInEx] Loading [Combo Indicator 1.5.2.0]` 16 | 17 | ## How to uninstall 18 | - Delete the folder `BepInEx\plugins\Combo Indicator`. 19 | 20 | ## How to use 21 | ### Default usage 22 | With the tweak loaded for the first time, you'll have a `com.biendeo.comboindicator.layout.xml` appear in your `BepInEx\config` folder. This file defines the layout used for all the elements in this tweak. If you begin playing any song, you should notice a "Hot Start" indicator if you hit the first 25 notes of the song without breaking a combo, a note streak indicator at 50, 100, 200, etc. notes, and a "Star Power Active" indicator when you can activate it. 23 | 24 | ### Configuring 25 | You can press `Ctrl + Shift + F8` to open the configuration menu at any time in Clone Hero. This lets you change several general settings of the program, such as where the indicator appears on screen, which events would trigger the indicator, and the keybind for opening this prompt. 26 | 27 | **The config is saved only when you press the `Save Config` button**, so if you decide to close your game before closing the window, your settings will not be saved. If you wish to reset the config, just simply delete `com.biendeo.comboindicator.config.xml` (or rename or changing anything such that the file doesn't exist with its original name), and run Clone Hero again. Several defaults of the tweak are based on your screen resolution at the time, so if elements are a bit oddly positioned, try resetting your config. 28 | 29 | Clicking `Test Indicator` will flash up a test messsage. This'll let you test how the indicator will look when it activates in-game. -------------------------------------------------------------------------------- /Tools/BiendeoCHLibValidator/BiendeoCHLibValidator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C4D77D40-30E6-44E1-B3A8-33ADB8326452} 8 | Exe 9 | BiendeoCHLibValidator 10 | BiendeoCHLibValidator 11 | v4.8 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | False 38 | ..\..\CH-Libraries\Assembly-CSharp.dll 39 | 40 | 41 | ..\..\BepInEx-Libraries\BepInEx.dll 42 | 43 | 44 | ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | {9b89d9f0-9d7f-4ab0-863a-c9bdeeec91bc} 63 | BiendeoCHLib 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/MainMenuWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using Rewired.UI.ControlMapper; 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Text; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using TMPro; 13 | using UnityEngine; 14 | 15 | namespace BiendeoCHLib.Wrappers { 16 | [Wrapper(typeof(MainMenu))] 17 | public struct MainMenuWrapper { 18 | public MainMenu MainMenu { get; private set; } 19 | 20 | public static MainMenuWrapper Wrap(MainMenu mainMenu) => new MainMenuWrapper { 21 | MainMenu = mainMenu 22 | }; 23 | 24 | public override bool Equals(object obj) => MainMenu.Equals(obj); 25 | 26 | public override int GetHashCode() => MainMenu.GetHashCode(); 27 | 28 | public bool IsNull() => MainMenu == null; 29 | 30 | #region Constructors 31 | 32 | public static MainMenuWrapper Construct() => new MainMenuWrapper { 33 | MainMenu = (MainMenu)defaultConstructor.Invoke(Array.Empty()) 34 | }; 35 | [WrapperConstructor] 36 | private static readonly ConstructorInfo defaultConstructor; 37 | 38 | #endregion 39 | 40 | #region Fields 41 | 42 | public GameObject SongSelect { 43 | get => songSelectField(MainMenu); 44 | set => songSelectField(MainMenu) = value; 45 | } 46 | [WrapperField("songSelect")] 47 | private static readonly AccessTools.FieldRef songSelectField; 48 | 49 | public GameObject SettingsMenu { 50 | get => settingsMenuField(MainMenu); 51 | set => settingsMenuField(MainMenu) = value; 52 | } 53 | [WrapperField("settingsMenu")] 54 | private static readonly AccessTools.FieldRef settingsMenuField; 55 | 56 | public GameObject OnlineMenu { 57 | get => onlineMenuField(MainMenu); 58 | set => onlineMenuField(MainMenu) = value; 59 | } 60 | [WrapperField("onlineMenu")] 61 | private static readonly AccessTools.FieldRef onlineMenuField; 62 | 63 | public SongScanWrapper SongScan { 64 | get => SongScanWrapper.Wrap(songScanField(MainMenu)); 65 | set => songScanField(MainMenu) = value.SongScan; 66 | } 67 | [WrapperField("songScan")] 68 | private static readonly AccessTools.FieldRef songScanField; 69 | 70 | public ControlMapper ControlMapper { 71 | get => controlMapperField(MainMenu); 72 | set => controlMapperField(MainMenu) = value; 73 | } 74 | [WrapperField("controlMapper")] 75 | private static readonly AccessTools.FieldRef controlMapperField; 76 | 77 | public ConfirmationMenuWrapper ConfirmMenu { 78 | get => ConfirmationMenuWrapper.Wrap(confirmMenuField(MainMenu)); 79 | set => confirmMenuField(MainMenu) = value.ConfirmationMenu; 80 | } 81 | [WrapperField("confirmMenu")] 82 | private static readonly AccessTools.FieldRef confirmMenuField; 83 | 84 | public ConfirmationMenuWrapper NotSupportedMenu { 85 | get => ConfirmationMenuWrapper.Wrap(notSupportedMenuField(MainMenu)); 86 | set => notSupportedMenuField(MainMenu) = value.ConfirmationMenu; 87 | } 88 | [WrapperField("notSupportedMenu")] 89 | private static readonly AccessTools.FieldRef notSupportedMenuField; 90 | 91 | #endregion 92 | 93 | #region Methods 94 | 95 | #endregion 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.5.2.{build} 2 | 3 | image: Visual Studio 2019 4 | clone_depth: 1 5 | 6 | branches: 7 | only: 8 | - master 9 | 10 | skip_tags: true 11 | 12 | cache: 13 | - BepInEx-Libraries 14 | - CH-Libraries 15 | 16 | install: 17 | - echo Downloading Clone Hero 18 | - ps: | 19 | if (-not (Test-Path .\CH-Libraries)) { 20 | Start-FileDownload 'http://dl.clonehero.net/clonehero-v.23.2.2/clonehero-win64.7z' 21 | 7z x clonehero-win64.7z 22 | New-Item -ItemType Directory -Path 'CH-Libraries' 23 | Copy-Item -Path '.\clonehero-win64\Clone Hero_Data\Managed\*' -Destination 'CH-Libraries' -Recurse 24 | } 25 | - echo Downloading BepInEx 26 | - ps: | 27 | if (-not (Test-Path .\BepInEx-Libraries)) { 28 | Start-FileDownload 'https://github.com/BepInEx/BepInEx/releases/download/v5.4.17/BepInEx_x64_5.4.17.0.zip' 29 | 7z x BepInEx_x64_5.4.17.0.zip 30 | New-Item -ItemType Directory -Path 'BepInEx-Libraries' 31 | Copy-Item -Path '.\BepInEx\core\*' -Destination 'BepInEx-Libraries' -Recurse 32 | } 33 | 34 | assembly_info: 35 | patch: true 36 | file: AssemblyInfo.* 37 | assembly_version: "{version}" 38 | assembly_file_version: "{version}" 39 | 40 | platform: Any CPU 41 | configuration: 42 | - Debug 43 | - Release 44 | 45 | before_build: 46 | - nuget restore 47 | 48 | after_build: 49 | - ps: | 50 | @("AccuracyIndicator", "BiendeoCHLib", "ComboIndicator", "ExtraSongUI", "LegacyModLoader", "PerfectMode", "SplashTextEditor") | % { 51 | New-Item -Type Directory -Path ".\Artifacts\$env:CONFIGURATION\$_\BepInEx\plugins\$_" 52 | Copy-Item -Path ".\$_\bin\$env:CONFIGURATION\$_.dll" -Destination ".\Artifacts\$env:CONFIGURATION\$_\BepInEx\plugins\$_" 53 | 7z a .\Artifacts\$_$(if ($env:CONFIGURATION -eq "Release") { [string]::Empty } else { "-Debug" }).zip .\Artifacts\$env:CONFIGURATION\$_\BepInEx 54 | } 55 | 56 | artifacts: 57 | - path: Artifacts\AccuracyIndicator.zip 58 | name: AccuracyIndicator 59 | - path: Artifacts\AccuracyIndicator-Debug.zip 60 | name: AccuracyIndicator-Debug 61 | - path: Artifacts\BiendeoCHLib.zip 62 | name: BiendeoCHLib 63 | - path: Artifacts\BiendeoCHLib-Debug.zip 64 | name: BiendeoCHLib-Debug 65 | - path: Artifacts\ComboIndicator.zip 66 | name: ComboIndicator 67 | - path: Artifacts\ComboIndicator-Debug.zip 68 | name: ComboIndicator-Debug 69 | - path: Artifacts\ExtraSongUI.zip 70 | name: ExtraSongUI 71 | - path: Artifacts\ExtraSongUI-Debug.zip 72 | name: ExtraSongUI-Debug 73 | - path: Artifacts\LegacyModLoader.zip 74 | name: LegacyModLoader 75 | - path: Artifacts\LegacyModLoader-Debug.zip 76 | name: LegacyModLoader-Debug 77 | - path: Artifacts\PerfectMode.zip 78 | name: PerfectMode 79 | - path: Artifacts\PerfectMode-Debug.zip 80 | name: PerfectMode-Debug 81 | - path: Artifacts\SplashTextEditor.zip 82 | name: SplashTextEditor 83 | - path: Artifacts\SplashTextEditor-Debug.zip 84 | name: SplashTextEditor-Debug 85 | 86 | deploy: 87 | - provider: GitHub 88 | release: v$(APPVEYOR_BUILD_VERSION)-pre 89 | description: $(APPVEYOR_REPO_COMMIT_MESSAGE) 90 | auth_token: 91 | secure: VZ3WQUK/e5R7Roa5Vl0SRnK1Z1lrkWv861LeOpomdmdhi1x8SQtqpc8Pa5gomgbc 92 | artifact: /.*/ 93 | prerelease: true 94 | skip_tags: true 95 | on: 96 | branch: master 97 | APPVEYOR_REPO_TAG: false -------------------------------------------------------------------------------- /SplashTextEditor/SplashTextEditor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BA46AC28-F9CA-48CC-8ABC-AEF87DD06F1D} 8 | Library 9 | Properties 10 | SplashTextEditor 11 | SplashTextEditor 12 | v4.8 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | ..\CH-Libraries\Assembly-CSharp.dll 36 | 37 | 38 | ..\BepInEx-Libraries\BepInEx.dll 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | ..\CH-Libraries\Unity.TextMeshPro.dll 50 | 51 | 52 | ..\CH-Libraries\UnityEngine.dll 53 | 54 | 55 | ..\CH-Libraries\UnityEngine.CoreModule.dll 56 | 57 | 58 | ..\CH-Libraries\UnityEngine.IMGUIModule.dll 59 | 60 | 61 | ..\CH-Libraries\UnityEngine.TextRenderingModule.dll 62 | 63 | 64 | ..\CH-Libraries\UnityEngine.UI.dll 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {9b89d9f0-9d7f-4ab0-863a-c9bdeeec91bc} 75 | BiendeoCHLib 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /GigChallenges/ChallengeBar.cs: -------------------------------------------------------------------------------- 1 | using GigChallenges.Interfaces; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using UnityEngine; 8 | 9 | namespace GigChallenges { 10 | public class ChallengeBar : MonoBehaviour { 11 | private static Sprite spContainerSprite; 12 | private static Sprite barSprite; 13 | private SpriteRenderer challengeContainer; 14 | private SpriteRenderer lowerBar; 15 | 16 | private void Start() { 17 | if (spContainerSprite == null) { 18 | var go = GameObject.Find("spcontainer"); 19 | if (go != null) { 20 | spContainerSprite = go.GetComponent().sprite; 21 | } 22 | } 23 | if (barSprite == null) { 24 | var go = GameObject.Find("lowerbar"); 25 | if (go != null) { 26 | barSprite = go.GetComponent().sprite; 27 | } 28 | } 29 | 30 | transform.localPosition = new Vector3(-1.05f, 0.0f, 5.0f); 31 | transform.localScale = new Vector3(1.0f, 1.0f, 1.0f); 32 | gameObject.layer = 9; 33 | 34 | challengeContainer.transform.localPosition = new Vector3(0.54f, 0.0f, -1.5f); 35 | challengeContainer.transform.localEulerAngles = new Vector3(0.0f, 0.0f, 334.5f); 36 | challengeContainer.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f); 37 | challengeContainer.gameObject.layer = 9; 38 | 39 | challengeContainer.sprite = spContainerSprite; 40 | challengeContainer.renderingLayerMask = 4294967295; 41 | challengeContainer.sortingOrder = -500; 42 | 43 | lowerBar.transform.localPosition = new Vector3(0.52f, 0.0f, -1.45f); 44 | lowerBar.transform.localEulerAngles = new Vector3(0.0f, 0.0f, 333.0f); 45 | lowerBar.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f); 46 | lowerBar.gameObject.layer = 9; 47 | 48 | lowerBar.sprite = barSprite; 49 | lowerBar.renderingLayerMask = 4294967295; 50 | lowerBar.color = Color.green; 51 | } 52 | 53 | public void SetState(IChallenge challenge) { 54 | var functionsToBar = new (Func Func, SpriteRenderer LowerBar)[] { 55 | (() => challenge.PercentageToBronze, lowerBar) 56 | }; 57 | foreach (var f in functionsToBar) { 58 | float fillAmount = f.Func(); 59 | if (fillAmount <= 0.0f) { 60 | f.LowerBar.size = Vector2.zero; 61 | GigChallenges.InstanceLogger.LogDebug($"0.75 - 1 ({f.LowerBar.size})"); 62 | } else if (fillAmount >= 0.75f) { 63 | float num = (fillAmount - 0.75f) * 4.0f; 64 | f.LowerBar.size = new Vector2(0.04f, 0.196f); 65 | GigChallenges.InstanceLogger.LogDebug($"0.75 - 1 ({f.LowerBar.size})"); 66 | } else if (fillAmount >= 0.5f) { 67 | float num = (fillAmount - 0.5f) * 4.0f; 68 | f.LowerBar.size = new Vector2(0.04f, 0.196f); 69 | GigChallenges.InstanceLogger.LogDebug($"0.5 - 0.75 ({f.LowerBar.size})"); 70 | } else if (fillAmount >= 0.25f) { 71 | float num = (fillAmount - 0.25f) * 4.0f; 72 | f.LowerBar.size = new Vector2(0.04f, 0.091f * num + 0.105f); 73 | GigChallenges.InstanceLogger.LogDebug($"0.25 - 0.5 ({f.LowerBar.size})"); 74 | } else { 75 | float num = fillAmount * 4.0f; 76 | f.LowerBar.size = new Vector2(0.04f, 0.07f * num + 0.035f); 77 | GigChallenges.InstanceLogger.LogDebug($"0 - 0.25 ({f.LowerBar.size})"); 78 | } 79 | } 80 | } 81 | 82 | public static ChallengeBar InstantiatePrefab(Transform parent) { 83 | var challengeBar = new GameObject("Challenge Bar").AddComponent(); 84 | challengeBar.transform.SetParent(parent); 85 | 86 | challengeBar.challengeContainer = new GameObject("Challenge Container").AddComponent(); 87 | challengeBar.challengeContainer.transform.SetParent(challengeBar.transform); 88 | 89 | challengeBar.lowerBar = new GameObject("Lower Bar").AddComponent(); 90 | challengeBar.lowerBar.transform.SetParent(challengeBar.transform); 91 | 92 | return challengeBar; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /GigChallenges/GigChallenges.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {6B3DA986-F066-4CF2-A1AA-90D2CFF14725} 8 | Library 9 | Properties 10 | GigChallenges 11 | GigChallenges 12 | v4.8 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | ..\BepInEx-Libraries\0Harmony.dll 36 | 37 | 38 | 39 | ..\BepInEx-Libraries\BepInEx.dll 40 | 41 | 42 | False 43 | ..\CH-Libraries\Rewired_Core.dll 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | False 55 | ..\CH-Libraries\UnityEngine.dll 56 | 57 | 58 | False 59 | ..\CH-Libraries\UnityEngine.CoreModule.dll 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | {9B89D9F0-9D7F-4AB0-863A-C9BDEEEC91BC} 74 | BiendeoCHLib 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/INIParserWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | using System.Text; 7 | 8 | namespace BiendeoCHLib.Wrappers { 9 | [Wrapper("\u030E\u0317\u0316\u031A\u0318\u0314\u0315\u030F\u0315\u0313\u0311")] 10 | public struct INIParserWrapper { 11 | public object INIParser { get; private set; } 12 | 13 | public static INIParserWrapper Wrap(object iniParser) => new INIParserWrapper { 14 | INIParser = iniParser 15 | }; 16 | 17 | public override bool Equals(object obj) => INIParser.Equals(obj); 18 | 19 | public override int GetHashCode() => INIParser.GetHashCode(); 20 | 21 | public bool IsNull() => INIParser == null; 22 | 23 | #region Methods 24 | 25 | public void Open(string path) => openMethod(INIParser, path); 26 | [WrapperMethod("\u0318\u0318\u030F\u0316\u0314\u0317\u031A\u0314\u0310\u031A\u031B", new Type[] { typeof(string) })] 27 | private static readonly FastInvokeHandler openMethod; 28 | 29 | public void Close() => closeMethod(INIParser); 30 | [WrapperMethod("\u031A\u031C\u0318\u030E\u030F\u0317\u030E\u030D\u030D\u030D\u0315")] 31 | private static readonly FastInvokeHandler closeMethod; 32 | 33 | public string ReadValue(string SectionName, string Key, string DefaultValue) => (string)readValue1Method(INIParser, SectionName, Key, DefaultValue); 34 | [WrapperMethod("\u0312\u031A\u030F\u0319\u0312\u0316\u0318\u031C\u030E\u0316\u030F", new Type[] { typeof(string), typeof(string), typeof(string) })] 35 | private static readonly FastInvokeHandler readValue1Method; 36 | 37 | public bool ReadValue(string SectionName, string Key, bool DefaultValue) => (bool)readValue2Method(INIParser, SectionName, Key, DefaultValue); 38 | [WrapperMethod("\u0312\u031A\u030F\u0319\u0312\u0316\u0318\u031C\u030E\u0316\u030F", new Type[] { typeof(string), typeof(string), typeof(bool) })] 39 | private static readonly FastInvokeHandler readValue2Method; 40 | 41 | public int ReadValue(string SectionName, string Key, int DefaultValue) => (int)readValue3Method(INIParser, SectionName, Key, DefaultValue); 42 | [WrapperMethod("\u0312\u031A\u030F\u0319\u0312\u0316\u0318\u031C\u030E\u0316\u030F", new Type[] { typeof(string), typeof(string), typeof(int) })] 43 | private static readonly FastInvokeHandler readValue3Method; 44 | 45 | public long ReadValue(string SectionName, string Key, long DefaultValue) => (long)readValue4Method(INIParser, SectionName, Key, DefaultValue); 46 | [WrapperMethod("\u0312\u031A\u030F\u0319\u0312\u0316\u0318\u031C\u030E\u0316\u030F", new Type[] { typeof(string), typeof(string), typeof(long) })] 47 | private static readonly FastInvokeHandler readValue4Method; 48 | 49 | public double ReadValue(string SectionName, string Key, double DefaultValue) => (double)readValue5Method(INIParser, SectionName, Key, DefaultValue); 50 | [WrapperMethod("\u0312\u031A\u030F\u0319\u0312\u0316\u0318\u031C\u030E\u0316\u030F", new Type[] { typeof(string), typeof(string), typeof(double) })] 51 | private static readonly FastInvokeHandler readValue5Method; 52 | 53 | public byte[] ReadValue(string SectionName, string Key, byte[] DefaultValue) => (byte[])readValue6Method(INIParser, SectionName, Key, DefaultValue); 54 | [WrapperMethod("\u0312\u031A\u030F\u0319\u0312\u0316\u0318\u031C\u030E\u0316\u030F", new Type[] { typeof(string), typeof(string), typeof(byte[]) })] 55 | private static readonly FastInvokeHandler readValue6Method; 56 | 57 | public DateTime ReadValue(string SectionName, string Key, DateTime DefaultValue) => (DateTime)readValue7Method(INIParser, SectionName, Key, DefaultValue); 58 | [WrapperMethod("\u0312\u031A\u030F\u0319\u0312\u0316\u0318\u031C\u030E\u0316\u030F", new Type[] { typeof(string), typeof(string), typeof(DateTime) })] 59 | private static readonly FastInvokeHandler readValue7Method; 60 | 61 | public bool IsKeyExists(string SectionName, string Key) => (bool)isKeyExistsMethod(INIParser, SectionName, Key); 62 | [WrapperMethod("\u031C\u0310\u030D\u031C\u031C\u031C\u030D\u030D\u030D\u0318\u0317")] 63 | private static readonly FastInvokeHandler isKeyExistsMethod; 64 | 65 | #endregion 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /BiendeoCHLib/VersionCheck.cs: -------------------------------------------------------------------------------- 1 | using BepInEx.Configuration; 2 | using BiendeoCHLib.Settings; 3 | using BiendeoCHLib.Wrappers; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Net; 10 | using System.Reflection; 11 | using System.Text; 12 | using UnityEngine; 13 | using UnityEngine.Experimental.PlayerLoop; 14 | 15 | namespace BiendeoCHLib { 16 | public class VersionCheck : MonoBehaviour { 17 | private int windowId; 18 | private string assemblyName; 19 | public string AssemblyVersion; 20 | private Rect updateWindowRect; 21 | private static string latestVersion; 22 | 23 | private GUIStyle labelStyle; 24 | private GUIStyle buttonStyle; 25 | private GUIStyle windowStyle; 26 | 27 | public ConfigEntry SilenceUpdates; 28 | public bool HasVersionBeenChecked; 29 | public bool IsShowingUpdateWindow; 30 | 31 | public VersionCheck() { 32 | updateWindowRect = new Rect(Screen.width / 2 - 150.0f, Screen.height / 2 - 100.0f, 300.0f, 200.0f); 33 | HasVersionBeenChecked = false; 34 | IsShowingUpdateWindow = false; 35 | labelStyle = null; 36 | buttonStyle = null; 37 | windowStyle = null; 38 | latestVersion = null; 39 | } 40 | 41 | public void InitializeSettings(Assembly assembly, ConfigFile config) { 42 | AssemblyVersion = FileVersionInfo.GetVersionInfo(assembly.Location).ProductVersion; 43 | assemblyName = new FileInfo(assembly.Location).Name; 44 | 45 | SilenceUpdates = config.Bind("VersionCheck", "SilenceUpdates", false, "Whether this mod prompts you for any available updates"); 46 | } 47 | 48 | public void Awake() { 49 | windowId = UnityEngine.Random.Range(int.MinValue, int.MaxValue); 50 | } 51 | 52 | public void Start() { 53 | if (!SilenceUpdates.Value && latestVersion == null) { 54 | string intendedVersion = GlobalVariablesWrapper.Instance.BuildVersion; 55 | try { 56 | using (var wc = new WebClient()) { 57 | string versionsText = wc.DownloadString("https://raw.githubusercontent.com/Biendeo/My-Clone-Hero-Tweaks/master/versions.txt"); 58 | var versions = versionsText.Split('\n').Select(l => l.Split('=')); 59 | latestVersion = versions.Single(l => l[0] == intendedVersion)[1]; 60 | 61 | } 62 | } catch (WebException) { 63 | // Any WebException could cause an error; since it's not really too vital for the tweak, it's simpler to just not prompt for an update. 64 | } catch (InvalidOperationException) { 65 | // This exception is thrown if the CH version isn't found in the versions list. Perhaps it should prompt the user that they're using an unsupported CH version? 66 | } 67 | } 68 | if (latestVersion != null) { 69 | IsShowingUpdateWindow = latestVersion != string.Join(".", AssemblyVersion.Split('.').Take(3)); 70 | } 71 | HasVersionBeenChecked = true; 72 | } 73 | 74 | public void OnGUI() { 75 | if (labelStyle == null || buttonStyle == null || windowStyle == null) { 76 | labelStyle = new GUIStyle(GUI.skin.label); 77 | buttonStyle = new GUIStyle(GUI.skin.button); 78 | windowStyle = new GUIStyle(GUI.skin.window); 79 | } 80 | if (IsShowingUpdateWindow) { 81 | var r = GUILayout.Window(windowId, updateWindowRect, DrawWindow, new GUIContent($"Tweak Updated Required!"), windowStyle); 82 | updateWindowRect.x = r.x; 83 | updateWindowRect.y = r.y; 84 | } 85 | } 86 | 87 | private void DrawWindow(int id) { 88 | var largeLabelStyle = new GUIStyle(labelStyle) { 89 | fontSize = 23, 90 | alignment = TextAnchor.MiddleCenter, 91 | normal = new GUIStyleState { 92 | textColor = Color.white, 93 | } 94 | }; 95 | GUILayout.Label($"{assemblyName} has an update available!", largeLabelStyle) ; 96 | GUILayout.Label($"Please update to version {latestVersion}!", largeLabelStyle); 97 | if (GUILayout.Button("Open update page", buttonStyle)) { 98 | Application.OpenURL($"https://github.com/Biendeo/My-Clone-Hero-Tweaks/releases/tag/v{latestVersion}"); 99 | } 100 | if (GUILayout.Button("Close this window", buttonStyle)) { 101 | IsShowingUpdateWindow = false; 102 | } 103 | GUI.DragWindow(); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /PerfectMode/README.md: -------------------------------------------------------------------------------- 1 | # Perfect Mode 2 | Hate having to notice that you missed a note, pausing, and scrolling to restart, and pressing green twice? Have the game do it for you! 3 | With one easy tweak, you can now toggle whether the game automatically restarts when you botch up a note. Too harsh? Just tune it down to any note amount for you! 4 | By pressing `Ctrl + Shift + F6`, you can bring up a window that allows you to customise how many notes you can miss, how long before the song restarts, and how the on-screen indicators look! 5 | 6 | ## Preview 7 | ![Perfect Mode Preview 1 (gfycat)](https://giant.gfycat.com/FearlessGlumElectriceel.gif) 8 | ![Perfect Mode Preview 2 (gfycat)](https://giant.gfycat.com/ThriftySereneJumpingbean.gif) 9 | 10 | ## How to install 11 | - You will need Clone Hero **v0.23.2.2**. You can use any version from the website (i.e. Win64, Win32, Mac, or Linux), but your mileage with the launcher version may vary. 12 | - Install [BepInEx v5.4.17](https://github.com/BepInEx/BepInEx/releases/tag/v5.4.17) into your Clone Hero directory. 13 | - Download the appropriate version and extract **all** of its files into your Clone Hero directory. 14 | - Please verify that BepInEx has initialised by running the game after extracting, and then checking that there are five folders and a `LogOutput.log` file inside the `BepInEx` folder. One of those folders will be named `plugins`, and you'll need that to run the mods. 15 | - Go to the [Releases page](https://github.com/Biendeo/My-Clone-Hero-Tweaks/releases) and download the latest version of Perfect Mode you want for your version of Clone Hero. 16 | - All the downloads are `.zip` files and will need to be extracted to your Clone Hero directory. They should merge with the existing `BepInEx` folder. You can verify it's in the correct place if `BepInEx\plugins\Perfect Mode` exists. 17 | - You will also need `Biendeo CH Lib` installed before this mod works! 18 | - To ensure that the mods have been extracted properly, check that `LogOutput.log` (or `LogOutput.log.1`, whichever is newer) has this line: `[Info : BepInEx] Loading [Perfect Mode 1.5.2.0]` 19 | 20 | ## How to uninstall 21 | - Delete the folder `BepInEx\plugins\Perfect Mode`. 22 | 23 | ## How to use 24 | ### Default usage 25 | With the tweak loaded for the first time, you'll have a `com.biendeo.perfectmode.config.xml` appear in your `BepInEx\config` folder. This file defines the layout used for the UI elements of this tweak, as well as enabling or disabling the perfect mode. 26 | 27 | **By default perfect mode is off!** You need to enable it by pressing `Ctrl + Shift + F6` to open the configuration menu. Up the top is `Enabled`, which determines whether the mode is on or off. To assist with knowing whether this mode is on or off, you can check `On-screen Indicator`, which will show a message on-screen when your have perfect mode on. 28 | 29 | You can also control whether the perfect mode restart kicks in when you break an FC, 100%, or any other number of notes missed. When `FC Mode` is checked, you must maintain a full combo in order to successfully pass the song. If you uncheck it, then the game will only restart the song when you miss more notes than the `Note Miss Limit`. You can also tweak the `Fail Delay Before Restart` to change how long it takes before the game restarts the song. 30 | 31 | ### Configuring 32 | You can press `Ctrl + Shift + F6` to open the configuration menu at any time in Clone Hero. This lets you access the details of every element used by the tweak, from the position, colour, size, alignment, and formatting. It also lets you change some of the settings of the tweak's functioanlity. Simply navigate the menus and change the details of settings as you please. The elements on-screen will change in real time to help you create the layout you desire. 33 | 34 | **The config is saved only when you press the `Save Config` button**, so if you decide to close your game before closing the window, your settings will not be saved. If you wish to reset the config, just simply delete `com.biendeo.perfectmode.config.xml` (or rename or changing anything such that the file doesn't exist with its original name), and run Clone Hero again. Several defaults of the tweak are based on your screen resolution at the time, so if elements are a bit oddly positioned, try resetting your config. -------------------------------------------------------------------------------- /ComboIndicator/DancingText.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Settings; 2 | using BiendeoCHLib.Wrappers; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using UnityEngine; 9 | using UnityEngine.UI; 10 | 11 | namespace ComboIndicator { 12 | public class DancingText : MonoBehaviour { 13 | public bool IsTest; 14 | public string Text; 15 | public Font Font; 16 | internal GameManagerWrapper GameManager; 17 | public ColorablePositionableLabel LabelSettings; 18 | private Text text; 19 | 20 | private float timeAlive; 21 | private const float timeToLive = 170.0f / 60.0f; 22 | 23 | private static readonly float[,] keyframes = new float[,] { 24 | { 25 | 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f 26 | }, 27 | { 28 | 14.0f / 60.0f, 0.0f, 0.1f, 0.0f, 0.5f, 0.5f, 1.0f 29 | }, 30 | { 31 | 40.0f / 60.0f, 0.0f, 0.1f, -7.0f, 0.8f, 0.8f, 1.0f 32 | }, 33 | { 34 | 64.0f / 60.0f, 0.0f, 0.1f, 0.0f, 0.5f, 0.5f, 1.0f 35 | }, 36 | { 37 | 84.0f / 60.0f, 0.0f, 0.1f, 5.5f, 0.7f, 0.7f, 1.0f 38 | }, 39 | { 40 | 102.0f / 60.0f, 0.0f, 0.1f, 0.0f, 0.5f, 0.5f, 1.0f 41 | }, 42 | { 43 | 116.0f / 60.0f, 0.0f, 0.1f, -3.0f, 0.6f, 0.6f, 1.0f 44 | }, 45 | { 46 | 132.0f / 60.0f, 0.0f, 0.1f, 0.0f, 0.5f, 0.5f, 1.0f 47 | }, 48 | { 49 | 140.0f / 60.0f, 0.0f, 0.1f, 2.5f, 0.55f, 0.55f, 1.0f 50 | }, 51 | { 52 | 148.0f / 60.0f, 0.0f, 0.1f, 0.0f, 0.5f, 0.5f, 1.0f 53 | }, 54 | { 55 | 170.0f / 60.0f, 0.0f, 1.1f, 0.0f, 0.5f, 0.5f, 1.0f 56 | } 57 | }; 58 | 59 | public void Start() { 60 | Transform canvasTransform = null; 61 | foreach (var gameObject in gameObject.scene.GetRootGameObjects()) { 62 | if (gameObject.GetComponent() != null) { 63 | canvasTransform = gameObject.transform; 64 | } 65 | } 66 | gameObject.layer = LayerMask.NameToLayer("UI"); 67 | transform.SetParent(canvasTransform); 68 | transform.localPosition = new Vector3(); 69 | transform.localEulerAngles = new Vector3(); 70 | transform.localScale = new Vector3(1.0f, 1.0f, 1.0f); 71 | text = gameObject.AddComponent(); 72 | text.text = Text; 73 | text.font = Font; 74 | text.fontStyle = (LabelSettings.Bold ? FontStyle.Bold : FontStyle.Normal) | (LabelSettings.Italic ? FontStyle.Italic : FontStyle.Normal); 75 | text.color = LabelSettings.Color.Color; 76 | text.fontSize = LabelSettings.Size; 77 | text.alignment = LabelSettings.Alignment; 78 | text.horizontalOverflow = HorizontalWrapMode.Overflow; 79 | text.verticalOverflow = VerticalWrapMode.Overflow; 80 | timeAlive = 0.0f; 81 | } 82 | 83 | public void Update() { 84 | if (IsTest || GameManager.IsNull() || !GameManager.IsPaused) { 85 | timeAlive += Time.deltaTime; 86 | } 87 | if (timeAlive > timeToLive) { 88 | Destroy(gameObject); 89 | } else { 90 | int foundIndex = 0; 91 | for (int i = 1; i < keyframes.GetLength(0); ++i) { 92 | if (keyframes[i, 0] > timeAlive) { 93 | foundIndex = i; 94 | break; 95 | } 96 | } 97 | if (foundIndex == 0) { 98 | return; 99 | } else { 100 | float t = Mathf.SmoothStep(0.0f, 1.0f, (timeAlive - keyframes[foundIndex - 1, 0]) / (keyframes[foundIndex, 0] - keyframes[foundIndex - 1, 0])); 101 | float oneMinusT = 1 - t; 102 | 103 | transform.localPosition = new Vector3((keyframes[foundIndex - 1, 1] * oneMinusT + keyframes[foundIndex, 1] * t) * Screen.width + LabelSettings.X, (keyframes[foundIndex - 1, 2] * oneMinusT + keyframes[foundIndex, 2] * t) * Screen.height + LabelSettings.Y); 104 | transform.localEulerAngles = new Vector3(0.0f, 0.0f, keyframes[foundIndex - 1, 3] * oneMinusT + keyframes[foundIndex, 3] * t); 105 | transform.localScale = new Vector3((keyframes[foundIndex - 1, 4] * oneMinusT + keyframes[foundIndex, 4] * t) * Screen.width / 2560.0f, (keyframes[foundIndex - 1, 5] * oneMinusT + keyframes[foundIndex, 5] * t) * Screen.height / 1440.0f); 106 | text.color = new Color(LabelSettings.Color.Color.r, LabelSettings.Color.Color.g, LabelSettings.Color.Color.b, (keyframes[foundIndex - 1, 6] * oneMinusT + keyframes[foundIndex, 6] * t) * LabelSettings.Color.Color.a); 107 | } 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /SplashTextEditor/README.md: -------------------------------------------------------------------------------- 1 | # Splash Text Editor 2 | Are you tired of seeing how hungry the cache is? Don't want to go to AGDQ 2020? Don't like messages about bugs? 3 | With one easy tweak, you can now write your own splash messages for the title screen! If you want, you can also add your messages to the regular rotation of splash messages as well! 4 | By pressing `Ctrl + Shift + F9`, you can bring up a window that allows you to customise your splash messages! 5 | 6 | ## Preview 7 | ![Splash Text Editor Preview 1 (imgur)](https://i.imgur.com/eYHVvym.png) 8 | 9 | ## How to install 10 | - You will need Clone Hero **v0.23.2.2**. You can use any version from the website (i.e. Win64, Win32, Mac, or Linux), but your mileage with the launcher version may vary. 11 | - Install [BepInEx v5.4.17](https://github.com/BepInEx/BepInEx/releases/tag/v5.4.17) into your Clone Hero directory. 12 | - Download the appropriate version and extract **all** of its files into your Clone Hero directory. 13 | - Please verify that BepInEx has initialised by running the game after extracting, and then checking that there are five folders and a `LogOutput.log` file inside the `BepInEx` folder. One of those folders will be named `plugins`, and you'll need that to run the mods. 14 | - Go to the [Releases page](https://github.com/Biendeo/My-Clone-Hero-Tweaks/releases) and download the latest version of Splash Text Editor you want for your version of Clone Hero. 15 | - All the downloads are `.zip` files and will need to be extracted to your Clone Hero directory. They should merge with the existing `BepInEx` folder. You can verify it's in the correct place if `BepInEx\plugins\Splash Text Editor` exists. 16 | - You will also need `Biendeo CH Lib` installed before this mod works! 17 | - To ensure that the mods have been extracted properly, check that `LogOutput.log` (or `LogOutput.log.1`, whichever is newer) has this line: `[Info : BepInEx] Loading [Splash Text Editor 1.5.2.0]` 18 | 19 | ## How to uninstall 20 | - Delete the folder `BepInEx\plugins\Splash Text Editor`. 21 | 22 | ## How to use 23 | ### Default usage 24 | With the tweak loaded for the first time, you'll have a `com.biendeo.splashtexteditor.config.xml` appear in your `BepInEx\config` folder. This file describes what splash texts you've defined, as well as some other settings to tweak. 25 | 26 | **By default there are no custom splash messages!** You can press `Ctrl + Shift + F9` to bring up the editor menu, where you can edit some settings about the splash messages, as well as edit any custom splash messages. 27 | 28 | ### Configuring 29 | You can press `Ctrl + Shift + F9` to open the configuration menu at any time in Clone Hero. At the top of the configuration menu is an "*Enabled*" setting, which enables/disables the custom splash message functionality. If enabled, your custom splash messages will be displayed. You can also tick "*Vanilla Splash Messages*" if you'd like to see the messages the game itself defines. You can also tick "*DragonForce Override*" and "*April Fools Splashes*" if you'd like the see those special messages at the appropriate times (if you don't know what they are, it'll be a mystery). You can also change the cycle time (the time each splash message will be displayed on screen). 30 | 31 | Underneath is where you can customise the splash messages. Click "*Insert*" to add a new textbox where you can type your message. You can also delete messages, as well as shift them up or down for organisation. Do note that since splash messages are random, this is just for your neatness. You can also click "*Preview message*" to see it on the actual title screen. The splash messages will return to randomly cycling if you click "*Reset*" above, or when you change scenes (such as going into a song or the credits). 32 | 33 | **NOTE: Typing into the text areas will also interact with the game underneath, so please make sure all your user profiles are dropped out before typing anything to prevent accidentally going into a bunch of menus! Also, beware hitting Enter because that is often the default for assigning a profile.** 34 | 35 | **The config is saved only when you press the `Save Config` button**, so if you decide to close your game before closing the window, your settings will not be saved. If you wish to reset the config, just simply delete `com.biendeo.splashtexteditor.config.xml` (or rename or changing anything such that the file doesn't exist with its original name), and run Clone Hero again. -------------------------------------------------------------------------------- /ComboIndicator/Settings/Config.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Settings; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Xml.Serialization; 10 | using UnityEngine; 11 | 12 | namespace ComboIndicator.Settings { 13 | [Serializable] 14 | public class Config : IGUIConfigurable { 15 | public int Version; 16 | public string TweakVersion; 17 | public bool SilenceUpdates; 18 | 19 | public float ConfigX; 20 | public float ConfigY; 21 | public KeyBind ConfigKeyBind; 22 | 23 | public bool NoteStreakEnabled; 24 | public bool HotStartEnabled; 25 | public bool StarPowerActiveEnabled; 26 | 27 | public ColorablePositionableLabel Indicator; 28 | 29 | [XmlIgnore] 30 | public bool ConfigWindowEnabled; 31 | [XmlIgnore] 32 | public bool SeenChangelog; 33 | [XmlIgnore] 34 | private bool wasMouseVisible; 35 | 36 | public Config() { 37 | Version = 1; 38 | TweakVersion = "0.0.0"; 39 | SilenceUpdates = false; 40 | 41 | ConfigX = 300.0f; 42 | ConfigY = 200.0f; 43 | ConfigKeyBind = new KeyBind { 44 | Key = KeyCode.F8, 45 | Ctrl = true, 46 | Alt = false, 47 | Shift = true 48 | }; 49 | 50 | NoteStreakEnabled = true; 51 | HotStartEnabled = true; 52 | StarPowerActiveEnabled = true; 53 | 54 | Indicator = new ColorablePositionableLabel { 55 | Visible = true, 56 | X = 0, 57 | Y = 0, 58 | Size = 100, 59 | Bold = true, 60 | Italic = false, 61 | Alignment = TextAnchor.MiddleCenter, 62 | Color = new ColorARGB(Color.white) 63 | }; 64 | } 65 | 66 | public static Config LoadConfig(string configPath) { 67 | var configFilePath = new FileInfo(configPath); 68 | if (configFilePath.Exists) { 69 | var configString = File.ReadAllText(configFilePath.FullName); 70 | var serializer = new XmlSerializer(typeof(Config)); 71 | using (var configIn = new MemoryStream(Encoding.Unicode.GetBytes(configString))) { 72 | return serializer.Deserialize(configIn) as Config; 73 | } 74 | } else { 75 | var c = new Config(); 76 | c.SaveConfig(configPath); 77 | return c; 78 | } 79 | } 80 | 81 | public void ReloadConfig(string configPath) { 82 | var configFilePath = new FileInfo(configPath); 83 | if (configFilePath.Exists) { 84 | var configString = File.ReadAllText(configFilePath.FullName); 85 | var serializer = new XmlSerializer(typeof(Config)); 86 | using (var configIn = new MemoryStream(Encoding.Unicode.GetBytes(configString))) { 87 | var newConfig = serializer.Deserialize(configIn) as Config; 88 | ConfigKeyBind = newConfig.ConfigKeyBind; 89 | NoteStreakEnabled = newConfig.NoteStreakEnabled; 90 | HotStartEnabled = newConfig.HotStartEnabled; 91 | StarPowerActiveEnabled = newConfig.StarPowerActiveEnabled; 92 | Indicator = newConfig.Indicator; 93 | } 94 | } 95 | } 96 | 97 | public void SaveConfig(string configPath) { 98 | var configFilePath = new FileInfo(configPath); 99 | var serializer = new XmlSerializer(typeof(Config)); 100 | using (var configOut = configFilePath.Open(FileMode.Create)) { 101 | serializer.Serialize(configOut, this); 102 | } 103 | } 104 | 105 | public void HandleInput() { 106 | if (ConfigKeyBind.IsPressed && !ConfigKeyBind.JustSet) { 107 | ConfigWindowEnabled = !ConfigWindowEnabled; 108 | if (ConfigWindowEnabled) { 109 | wasMouseVisible = Cursor.visible; 110 | Cursor.visible = true; 111 | } else { 112 | if (!wasMouseVisible) Cursor.visible = false; 113 | } 114 | } 115 | ConfigKeyBind.JustSet = false; 116 | } 117 | 118 | public void ConfigureGUI(GUIConfigurationStyles styles) { 119 | GUILayout.Label("Settings", styles.LargeLabel); 120 | GUILayout.Space(25.0f); 121 | GUILayout.Label("Configuration Keybind", styles.LargeLabel); 122 | ConfigKeyBind.ConfigureGUI(styles); 123 | 124 | GUILayout.Space(25.0f); 125 | GUILayout.Label("Enable Features", styles.LargeLabel); 126 | NoteStreakEnabled = GUILayout.Toggle(NoteStreakEnabled, "Note Streak", styles.Toggle); 127 | HotStartEnabled = GUILayout.Toggle(HotStartEnabled, "Hot Start", styles.Toggle); 128 | StarPowerActiveEnabled = GUILayout.Toggle(StarPowerActiveEnabled, "Star Power Active", styles.Toggle); 129 | GUILayout.Label("Prevents the labels from fading out", new GUIStyle(styles.SmallLabel) { 130 | fontStyle = FontStyle.Italic 131 | }); 132 | 133 | if (GUILayout.Button("Test Indicator", styles.Button)) { 134 | ComboIndicator.Instance.CreateDancingText("Here is a test!", true); 135 | } 136 | 137 | GUILayout.Space(25.0f); 138 | Indicator.ConfigureGUI(styles); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # My Clone Hero Tweaks 2 | [![Build status](https://ci.appveyor.com/api/projects/status/cslq1e2b6b1rikpl?svg=true)](https://ci.appveyor.com/project/Biendeo/my-clone-hero-tweaks) 3 | 4 | 5 | A bunch of Clone Hero tweaks I have made! 6 | 7 | **More information about each tweak can be found in their respective folders.** 8 | 9 | ## Preview 10 | ![My Clone Hero Tweaks (gfycat)](https://giant.gfycat.com/GrouchyEarlyIggypops.gif) 11 | 12 | ## How to install 13 | - You will need Clone Hero **v0.23.2.2**. You can use any version from the website (i.e. Win64, Win32, Mac, or Linux), but your mileage with the launcher version may vary. 14 | - Install [BepInEx v5.4.17](https://github.com/BepInEx/BepInEx/releases/tag/v5.4.17) into your Clone Hero directory. 15 | - Download the appropriate version and extract **all** of its files into your Clone Hero directory. 16 | - Please verify that BepInEx has initialised by running the game after extracting, and then checking that there are five folders and a `LogOutput.log` file inside the `BepInEx` folder. One of those folders will be named `plugins`, and you'll need that to run the mods. 17 | - Go to the [Releases page](https://github.com/Biendeo/My-Clone-Hero-Tweaks/releases) and download the latest versions of the mods you want for your version of Clone Hero. 18 | - Almost all the mods will require `Biendeo CH Lib` to also be installed so please also download that. If you are missing it, the log will inform you. 19 | - All the downloads are `.zip` files and will need to be extracted to your Clone Hero directory. They should merge with the existing `BepInEx` folder. 20 | - To ensure that the mods have been extracted properly, check that `LogOutput.log` (or `LogOutput.log.1`, whichever is newer) has a line in this format for each mod: `[Info : BepInEx] Loading [Biendeo CH Lib 1.5.2.0]` 21 | 22 | ## How to reset configs 23 | - All mod configs are in `BepInEx\config`, so just delete the appropriate files. All mods have confings in the format `com.biendeo.MODNAME.*`. For example, to reset `Extra Song UI`, delete `com.biendeo.extrasongui.xml` and `com.biendeo.extrasongui.layout.xml`. 24 | 25 | ## How to uninstall 26 | - If you're just mods, go to `BepInEx\plugins` and delete the folder for the appropriate mod you're removing. All configs are in `BepInEx\config` if you want to also remove those. 27 | - If you're uninstalling BepInEx, delete the whole `BepInEx` folder, and `winhttp.dll` from your Clone Hero directory. 28 | 29 | ## How to troubleshoot 30 | - **Always perform these steps before giving up, because the logs usually indicate what has gone wrong every time!** 31 | - BepInEx logs to `BepInEx\LogOutput.log` and `BepInEx\LogOutput.log.1`. If both files aren't there, BepInEx didn't initialise and you should try and reinstall it. If they are, open the latest of the two. 32 | - Ensure that there exists a line for every mod (including `Biendeo CH Lib`) in the log indicating that it's `Loading`. If not, then the mods probably aren't installed. BepInEx will look inside `BepInEx\plugins` for any `dll` files to load, so if you haven't extracted the mods properly, then they probably aren't even loading to begin with. 33 | - Try and find any lines starting with `ERROR`. If there are, then there's probably an actual issue with that mod and you should [report a bug report through GitHub](https://github.com/Biendeo/My-Clone-Hero-Tweaks/issues/new/choose). Please follow the format and fill out as much information as you can. 34 | - If a lot of lines start with `WARN` or you don't see anything too obvious, this may be a user error, so double check that you're utilising the mods properly. Otherwise, you can also [report a bug report through GitHub](https://github.com/Biendeo/My-Clone-Hero-Tweaks/issues/new/choose). 35 | - Clone Hero logs to `%USERPROFILE%\AppData\LocalLow\srylain Inc_\Clone Hero\output_log.txt`. Any legacy mods would also log there, but it's only useful for spotting anything Clone Hero related that's gone wrong now. 36 | - Check out the [Clone Hero Modding Discord](https://discord.gg/YsGNFEj) if you want someone (maybe myself) to talk and help you out with anything. 37 | 38 | ## How to contribute 39 | ### Ideas 40 | - [Request a new feature through GitHub](https://github.com/Biendeo/My-Clone-Hero-Tweaks/issues/new/choose), it's the easiest way. 41 | - If it's a new idea as well, you can pitch the idea to the [Clone Hero Modding Discord](https://discord.gg/YsGNFEj) for some general feedback. 42 | ### Programming 43 | - If you spot a bug, feel free to make a pull request and I can review it. The functionality of my mods is my say though, so if it changes behaviour I'll have to decide whether I'm keeping it or not. 44 | - If you want to just fork my mods, that's fine, just make sure you've also got the GPL-3.0 license with your code as well. 45 | - You are free to make new mods that utilise `Biendeo CH Lib` to access the underlying game data if you choose. 46 | - My development process is described on this repository's wiki if you'd like some additional pointers. -------------------------------------------------------------------------------- /GigChallenges/GigChallenges.cs: -------------------------------------------------------------------------------- 1 | using BepInEx; 2 | using BepInEx.Logging; 3 | using BiendeoCHLib; 4 | using BiendeoCHLib.Patches; 5 | using BiendeoCHLib.Patches.Attributes; 6 | using BiendeoCHLib.Wrappers; 7 | using GigChallenges.Challenges; 8 | using GigChallenges.Interfaces; 9 | using HarmonyLib; 10 | using Rewired; 11 | using System; 12 | using System.Collections.Generic; 13 | using System.Linq; 14 | using System.Reflection; 15 | using System.Text; 16 | using System.Threading.Tasks; 17 | using UnityEngine; 18 | using UnityEngine.SceneManagement; 19 | 20 | namespace GigChallenges { 21 | [HarmonyCHPatch(typeof(GameManagerWrapper), nameof(GameManagerWrapper.OnControllerDisconnected))] 22 | public class OnControllerDisconnectedHandler { 23 | [HarmonyCHPostfix] 24 | static void Postfix(ControllerStatusChangedEventArgs __0) { 25 | if (!__0.controller.isConnected) { 26 | GigChallenges.InstanceLogger.LogDebug("DING!"); 27 | } 28 | } 29 | } 30 | 31 | [HarmonyCHPatch(typeof(HealthContainerWrapper), nameof(HealthContainerWrapper.Start))] 32 | public class TemporaryHealthContainerLatch { 33 | [HarmonyCHPostfix] 34 | static void Postfix() { 35 | GigChallenges.Instance.SetupHealthBar(); 36 | } 37 | } 38 | 39 | [BepInPlugin("com.biendeo.gigchallenges", "Gig Challenges", "1.5.2")] 40 | [BepInDependency("com.biendeo.biendeochlib")] 41 | public class GigChallenges : BaseUnityPlugin { 42 | public static GigChallenges Instance { get; private set; } 43 | public static ManualLogSource InstanceLogger => Instance.Logger; 44 | 45 | private bool sceneChanged; 46 | private bool isGameplay; 47 | private bool hasSetupChallenge; 48 | 49 | private VersionCheck versionCheck; 50 | private Rect changelogRect; 51 | 52 | private Harmony Harmony; 53 | 54 | private IChallenge activeChallenge; 55 | 56 | private bool isBot; 57 | private HealthContainerWrapper healthContainer; 58 | private SPBarWrapper spBar; 59 | private ChallengeBar challengeBar; 60 | 61 | public GigChallenges() { 62 | Instance = this; 63 | Harmony = new Harmony("com.biendeo.gigchallenges"); 64 | PatchBase.InitializePatches(Harmony, Assembly.GetExecutingAssembly(), Logger); 65 | } 66 | 67 | public void Start() { 68 | SceneManager.activeSceneChanged += delegate (Scene _, Scene destination) { 69 | sceneChanged = true; 70 | isGameplay = destination.name == "Gameplay"; 71 | hasSetupChallenge = false; 72 | }; 73 | } 74 | 75 | public void Awake() { 76 | versionCheck = gameObject.AddComponent(); 77 | versionCheck.InitializeSettings(Assembly.GetExecutingAssembly(), Config); 78 | } 79 | 80 | public void LateUpdate() { 81 | if (sceneChanged) { 82 | sceneChanged = false; 83 | if (isGameplay) { 84 | var gameManager = GameManagerWrapper.Wrap(GameObject.Find("Game Manager").GetComponent()); 85 | if (!(isBot = gameManager.BasePlayers[0].Player.PlayerProfile.Bot.GetBoolValue)) { 86 | var challengeFactory = new ChallengeFactory(); 87 | activeChallenge = challengeFactory.CreateChallenge(gameManager); 88 | Logger.LogDebug(activeChallenge.ChallengeGoal); 89 | healthContainer = gameManager.BasePlayers[0].HealthContainer; 90 | healthContainer.CastToMonoBehaviour().gameObject.SetActive(true); 91 | Logger.LogDebug("Updated!"); 92 | 93 | var clonedSPBar = Instantiate(GameObject.Find("SPBar")); 94 | clonedSPBar.transform.localPosition = new Vector3(0.016f, 0.0f, 5.0f); 95 | clonedSPBar.transform.localScale = new Vector3(-1.0f, 1.0f, 1.0f); 96 | clonedSPBar.transform.GetChild(0).GetComponent().enabled = false; 97 | Destroy(clonedSPBar.transform.GetChild(4).gameObject); 98 | clonedSPBar.transform.GetChild(6).GetComponent().color = new Color(1.0f, 0.5f, 0.6f); 99 | clonedSPBar.transform.GetChild(7).GetComponent().color = new Color(1.0f, 0.5f, 0.6f); 100 | spBar = SPBarWrapper.Wrap(clonedSPBar.GetComponent()); 101 | challengeBar = ChallengeBar.InstantiatePrefab(GameObject.Find("SPBar").transform.parent); 102 | } 103 | } else { 104 | activeChallenge = null; 105 | } 106 | } 107 | if (isGameplay && hasSetupChallenge && !isBot) { 108 | activeChallenge.Update(); 109 | //Logger.LogDebug($"Just before I run SetState, I have value {Mathf.Clamp(activeChallenge.PercentageToGold, 0.00001f, 1.0f)}, last {healthContainer.LastHealth}, RYT {healthContainer.RedYellowThreshold}, YGT {healthContainer.YellowGreenThreshold}"); 110 | challengeBar.SetState(activeChallenge); 111 | healthContainer.SetState(activeChallenge.PercentageToGold); 112 | spBar.SetFill(activeChallenge.PercentageToBronze, false); 113 | } 114 | } 115 | 116 | public void SetupHealthBar() { 117 | healthContainer.LastHealth = activeChallenge.PercentageOfGoldIsSilver; 118 | healthContainer.RedYellowThreshold = activeChallenge.PercentageOfGoldIsBronze; 119 | healthContainer.YellowGreenThreshold = activeChallenge.PercentageOfGoldIsSilver; 120 | Logger.LogDebug("Setup!"); 121 | hasSetupChallenge = true; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /AccuracyIndicator/README.md: -------------------------------------------------------------------------------- 1 | # Accuracy Indicator 2 | Ever overstrummed in a rapid string of notes because you were slightly ahead or behind of the hit window? This indicator will tell you exactly how early/late you are when hitting every note! It even works in practice mode! 3 | By pressing `Ctrl + Shift + F7`, you can bring up a window that allows you to customise where the indicator is, and what colours it'll display! 4 | 5 | ## Preview 6 | ![Accuracy Indicator Preview 1 (gfycat)](https://giant.gfycat.com/CarefreeHonorableGourami.gif) 7 | 8 | ## How to install 9 | - You will need Clone Hero **v0.23.2.2**. You can use any version from the website (i.e. Win64, Win32, Mac, or Linux), but your mileage with the launcher version may vary. 10 | - Install [BepInEx v5.4.17](https://github.com/BepInEx/BepInEx/releases/tag/v5.4.17) into your Clone Hero directory. 11 | - Download the appropriate version and extract **all** of its files into your Clone Hero directory. 12 | - Please verify that BepInEx has initialised by running the game after extracting, and then checking that there are five folders and a `LogOutput.log` file inside the `BepInEx` folder. One of those folders will be named `plugins`, and you'll need that to run the mods. 13 | - Go to the [Releases page](https://github.com/Biendeo/My-Clone-Hero-Tweaks/releases) and download the latest version of Accuracy Indicator you want for your version of Clone Hero. 14 | - All the downloads are `.zip` files and will need to be extracted to your Clone Hero directory. They should merge with the existing `BepInEx` folder. You can verify it's in the correct place if `BepInEx\plugins\Accuracy Indicator` exists. 15 | - You will also need `Biendeo CH Lib` installed before this mod works! 16 | - To ensure that the mods have been extracted properly, check that `LogOutput.log` (or `LogOutput.log.1`, whichever is newer) has this line: `[Info : BepInEx] Loading [Accuracy Indicator 1.5.2.0]` 17 | 18 | ## How to uninstall 19 | - Delete the folder `BepInEx\plugins\Accuracy Indicator`. 20 | 21 | ## How to use 22 | ### Default usage 23 | With the tweak loaded for the first time, you'll have a `com.biendeo.accuracyindicator.config.xml` appear in your `BepInEx\config` folder. This file defines the layout used for the UI elements of this tweak, as well as enabling or disabling the accuracy indicator. 24 | 25 | The accuracy indicator should appear to the bottom right of the highway. You can customise where it is by pressing `Ctrl + Shift + F7` to open the configuration menu. Up the top is `Enabled`, which determines whether the mode is on or off. It should be enabled by default. As you play notes, you'll see the indicator pop up, telling you how early/late you were to the note. Negative times mean you were early, positive times mean you were late. 26 | 27 | Also, if the tweak is enabled in the configuration menu, you will get a count of how many notes you hit with each accuracy level (very late, perfect, etc.). For each accuracy level, you'll get listed the number of notes, as well as the overall percentage of notes. There is also a number in milliseconds showing what your most forgiving cutoff was for each level throughout the song. This prevents people from tweaking the cutoffs just as a song ends to make their results look good; you have to stick with your thresholds for the whole song. Along with the totals, these reset if you reset the song (either from the results screen or the pause menu), so if you want to test yourself, just remember to not change the cutoffs while in the middle of a song. 28 | 29 | ### Configuring 30 | You can press `Ctrl + Shift + F7` to open the configuration menu at any time in Clone Hero. This lets you access the details of every element used by the tweak, from the position, colour, size, alignment, and formatting. It also lets you change some of the settings of the tweak's functionality. Simply navigate the menus and change the details of settings as you please. The elements on-screen will change in real time to help you create the layout you desire. 31 | 32 | **The config is saved only when you press the `Save Config` button**,, so if you decide to close your game before closing the window, your settings will not be saved. If you wish to reset the config, just simply delete `com.biendeo.accuracyindicator.config.xml` (or rename or changing anything such that the file doesn't exist with its original name), and run Clone Hero again. Several defaults of the tweak are based on your screen resolution at the time, so if elements are a bit oddly positioned, try resetting your config. 33 | 34 | Enabling `Test Layout` at the top of the configuration window will make the indicator visible at all times. This is useful for helping you arrange the indicator where you want without it disappearing. This mode is not really intended for regular usage, so it is recommended to disable it when you are finished (or not, it's your indicator 🤷‍♂️). 35 | 36 | Each color's `Cutoff Time` refers to the amount of time you can be off for this color. By default, being slightly early/late is 0.01s from the note, early/late is 0.02s, and very early/late is 0.03s. For the maximum functionality, ensure that slightly times are less than the early/late times, which are less than the very early/late times. The maximum times you can use are 0.07s, where the note is outside the hit window. -------------------------------------------------------------------------------- /ExtraSongUI/ExtraSongUI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {6BA711CB-ED1D-49E7-B371-D06CCC215E2C} 8 | Library 9 | Properties 10 | ExtraSongUI 11 | ExtraSongUI 12 | v4.8 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | ..\BepInEx-Libraries\0Harmony.dll 36 | 37 | 38 | False 39 | ..\CH-Libraries\Assembly-CSharp.dll 40 | 41 | 42 | ..\BepInEx-Libraries\BepInEx.dll 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | False 54 | ..\CH-Libraries\Unity.TextMeshPro.dll 55 | 56 | 57 | False 58 | ..\CH-Libraries\UnityEngine.dll 59 | 60 | 61 | False 62 | ..\CH-Libraries\UnityEngine.AnimationModule.dll 63 | 64 | 65 | False 66 | ..\CH-Libraries\UnityEngine.CoreModule.dll 67 | 68 | 69 | False 70 | ..\CH-Libraries\UnityEngine.IMGUIModule.dll 71 | 72 | 73 | False 74 | ..\CH-Libraries\UnityEngine.TextRenderingModule.dll 75 | 76 | 77 | False 78 | ..\CH-Libraries\UnityEngine.UI.dll 79 | 80 | 81 | False 82 | ..\CH-Libraries\UnityEngine.UIModule.dll 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | {9b89d9f0-9d7f-4ab0-863a-c9bdeeec91bc} 94 | BiendeoCHLib 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /PerfectMode/PerfectMode.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {1B27BFCC-1305-439E-B304-9F215B75CB16} 8 | Library 9 | Properties 10 | PerfectMode 11 | PerfectMode 12 | v4.8 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | False 36 | ..\BepInEx-Libraries\0Harmony.dll 37 | 38 | 39 | False 40 | ..\CH-Libraries\Assembly-CSharp.dll 41 | 42 | 43 | ..\BepInEx-Libraries\BepInEx.dll 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | False 55 | ..\CH-Libraries\Unity.TextMeshPro.dll 56 | 57 | 58 | False 59 | ..\CH-Libraries\UnityEngine.dll 60 | 61 | 62 | False 63 | ..\CH-Libraries\UnityEngine.AnimationModule.dll 64 | 65 | 66 | False 67 | ..\CH-Libraries\UnityEngine.CoreModule.dll 68 | 69 | 70 | False 71 | ..\CH-Libraries\UnityEngine.IMGUIModule.dll 72 | 73 | 74 | False 75 | ..\CH-Libraries\UnityEngine.TextRenderingModule.dll 76 | 77 | 78 | False 79 | ..\CH-Libraries\UnityEngine.UI.dll 80 | 81 | 82 | False 83 | ..\CH-Libraries\UnityEngine.UIModule.dll 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | {9b89d9f0-9d7f-4ab0-863a-c9bdeeec91bc} 94 | BiendeoCHLib 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /ComboIndicator/ComboIndicator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {55140311-41D2-477C-9DD5-2CAB0C829EF7} 8 | Library 9 | Properties 10 | ComboIndicator 11 | ComboIndicator 12 | v4.8 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | False 36 | ..\BepInEx-Libraries\0Harmony.dll 37 | 38 | 39 | False 40 | ..\CH-Libraries\Assembly-CSharp.dll 41 | 42 | 43 | ..\BepInEx-Libraries\BepInEx.dll 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | False 55 | ..\CH-Libraries\Unity.TextMeshPro.dll 56 | 57 | 58 | False 59 | ..\CH-Libraries\UnityEngine.dll 60 | 61 | 62 | False 63 | ..\CH-Libraries\UnityEngine.AnimationModule.dll 64 | 65 | 66 | False 67 | ..\CH-Libraries\UnityEngine.CoreModule.dll 68 | 69 | 70 | False 71 | ..\CH-Libraries\UnityEngine.IMGUIModule.dll 72 | 73 | 74 | False 75 | ..\CH-Libraries\UnityEngine.TextRenderingModule.dll 76 | 77 | 78 | False 79 | ..\CH-Libraries\UnityEngine.UI.dll 80 | 81 | 82 | False 83 | ..\CH-Libraries\UnityEngine.UIModule.dll 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | {9b89d9f0-9d7f-4ab0-863a-c9bdeeec91bc} 95 | BiendeoCHLib 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /AccuracyIndicator/AccuracyIndicator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {90FE6B48-8612-42C6-82CC-D49A606BDCBA} 8 | Library 9 | Properties 10 | AccuracyIndicator 11 | AccuracyIndicator 12 | v4.8 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | ..\BepInEx-Libraries\0Harmony.dll 36 | 37 | 38 | False 39 | ..\CH-Libraries\Assembly-CSharp.dll 40 | 41 | 42 | ..\BepInEx-Libraries\BepInEx.dll 43 | 44 | 45 | ..\BepInEx-Libraries\BepInEx.Harmony.dll 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | False 57 | ..\CH-Libraries\Unity.TextMeshPro.dll 58 | 59 | 60 | False 61 | ..\CH-Libraries\UnityEngine.dll 62 | 63 | 64 | False 65 | ..\CH-Libraries\UnityEngine.AnimationModule.dll 66 | 67 | 68 | False 69 | ..\CH-Libraries\UnityEngine.CoreModule.dll 70 | 71 | 72 | False 73 | ..\CH-Libraries\UnityEngine.IMGUIModule.dll 74 | 75 | 76 | False 77 | ..\CH-Libraries\UnityEngine.TextRenderingModule.dll 78 | 79 | 80 | False 81 | ..\CH-Libraries\UnityEngine.UI.dll 82 | 83 | 84 | False 85 | ..\CH-Libraries\UnityEngine.UIModule.dll 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | {9b89d9f0-9d7f-4ab0-863a-c9bdeeec91bc} 97 | BiendeoCHLib 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/SongWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using UnityEngine; 10 | 11 | namespace BiendeoCHLib.Wrappers { 12 | [Wrapper("\u031B\u0317\u0310\u0316\u0319\u0313\u0312\u0316\u0313\u031B\u0310")] 13 | public struct SongWrapper { 14 | public object Song { get; private set; } 15 | 16 | public static SongWrapper Wrap(object song) => new SongWrapper { 17 | Song = song 18 | }; 19 | 20 | public override bool Equals(object obj) => Song.Equals(obj); 21 | 22 | public override int GetHashCode() => Song.GetHashCode(); 23 | 24 | public bool IsNull() => Song == null; 25 | 26 | #region Fields 27 | 28 | public MoonChartWrapper[] Charts { 29 | get => chartsField(Song).Select(o => MoonChartWrapper.Wrap(o)).ToArray(); 30 | set => chartsField(Song) = value.Select(o => o.MoonChart).ToArray(); 31 | } 32 | [WrapperField("\u030D\u0318\u0316\u0314\u0316\u0317\u0315\u0310\u0312\u0313\u0310")] 33 | private static readonly AccessTools.FieldRef chartsField; 34 | 35 | public static string QUOTEVALIDATE => (string)quoteValidateField.GetValue(null); //! Regex 36 | [WrapperField("\u030E\u0311\u0317\u0310\u0310\u031A\u0311\u0319\u030F\u0318\u0310")] 37 | private static readonly FieldInfo quoteValidateField; 38 | 39 | public string Genre { 40 | get => genreField(Song); 41 | set => genreField(Song) = value; 42 | } //! Rock? 43 | [WrapperField("\u0311\u0313\u0310\u030D\u030F\u030E\u0317\u0313\u031A\u030F\u0310")] 44 | private static readonly AccessTools.FieldRef genreField; 45 | 46 | public static string FLOATSEARCH => (string)floatSearchField.GetValue(null); //! Regex 47 | [WrapperField("\u0315\u030D\u0317\u0318\u0318\u030F\u031B\u0310\u0313\u0315\u030E")] 48 | private static readonly FieldInfo floatSearchField; 49 | 50 | public string UnknownString4 { 51 | get => unknownString4Field(Song); 52 | set => unknownString4Field(Song) = value; 53 | } //! String empty? 54 | [WrapperField("\u0316\u0316\u0317\u0310\u0315\u0312\u030F\u0313\u0316\u031A\u0319")] 55 | private static readonly AccessTools.FieldRef unknownString4Field; 56 | 57 | public string UnknownString5 { 58 | get => unknownString5Field(Song); 59 | set => unknownString5Field(Song) = value; 60 | } //! String empty? 61 | [WrapperField("\u0317\u0310\u0312\u0315\u031A\u031C\u030E\u031B\u0318\u031A\u0310")] 62 | private static readonly AccessTools.FieldRef unknownString5Field; 63 | 64 | public string UnknownString6 { 65 | get => unknownString6Field(Song); 66 | set => unknownString6Field(Song) = value; 67 | } //! String empty? 68 | [WrapperField("\u0318\u030F\u0314\u0314\u030D\u0316\u0319\u0312\u0316\u0313\u031C")] 69 | private static readonly AccessTools.FieldRef unknownString6Field; 70 | 71 | public string MediaType { 72 | get => mediaTypeField(Song); 73 | set => mediaTypeField(Song) = value; 74 | } //! cd 75 | [WrapperField("\u031A\u0314\u030F\u0318\u0315\u0319\u0313\u0319\u0311\u0312\u030E")] 76 | private static readonly AccessTools.FieldRef mediaTypeField; 77 | 78 | public string UnknownString8 { 79 | get => unknownString8Field(Song); 80 | set => unknownString8Field(Song) = value; 81 | } //! String empty? 82 | [WrapperField("\u031A\u031B\u0310\u0318\u031A\u031C\u030F\u0312\u0318\u031A\u031A")] 83 | private static readonly AccessTools.FieldRef unknownString8Field; 84 | 85 | public string Player2 { 86 | get => player2Field(Song); 87 | set => player2Field(Song) = value; 88 | } //! Bass 89 | [WrapperField("\u031B\u030E\u0310\u0315\u0312\u0311\u0310\u0312\u0319\u0313\u0311")] 90 | private static readonly AccessTools.FieldRef player2Field; 91 | 92 | public static string QUOTESEARCH => (string)quoteSearchField.GetValue(null); //! Regex 93 | [WrapperField("\u031B\u0314\u031C\u0312\u031A\u031C\u0313\u0319\u0318\u031B\u031A")] 94 | private static readonly FieldInfo quoteSearchField; 95 | 96 | #endregion 97 | 98 | #region Methods 99 | 100 | /// 101 | /// Returns a chart given an instrument and a difficulty. This should have no side effects as the method 102 | /// implementation is just returning a specific element of an array. 103 | /// The difficulty and instrument fields seem to just be plain wrong, so maybe ignore getting this. 104 | /// 105 | /// 106 | /// 107 | /// 108 | public MoonChartWrapper GetChart(sbyte instrument, sbyte difficulty) => MoonChartWrapper.Wrap(getChartMethod(Song, instrument, difficulty)); 109 | [WrapperMethod("\u030D\u0311\u0313\u0316\u0311\u0313\u0318\u030F\u0318\u0319\u0316")] 110 | private static readonly FastInvokeHandler getChartMethod; //TODO: Make this use the enums 111 | 112 | #endregion 113 | 114 | #region Enumerations 115 | 116 | [WrapperEnum("\u030F\u0312\u031A\u0318\u0311\u030F\u031B\u0313\u0311\u030E\u0310")] 117 | public enum Difficulty { 118 | Expert, 119 | Hard, 120 | Medium, 121 | Easy 122 | } 123 | 124 | [WrapperEnum("\u0315\u031C\u0313\u0319\u0310\u0313\u030F\u0311\u030F\u0319\u030F")] 125 | public enum Instrument { 126 | Guitar, 127 | GuitarCoop, 128 | Bass, 129 | Keys, 130 | Drums, 131 | GHLGuitar, 132 | GHLBass, 133 | Vocals, 134 | Crowd, 135 | None, 136 | Rhythm 137 | } 138 | 139 | #endregion 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/GameManagerWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using Rewired; 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using UnityEngine; 12 | 13 | namespace BiendeoCHLib.Wrappers { 14 | [Wrapper(typeof(GameManager))] 15 | public struct GameManagerWrapper { 16 | public GameManager GameManager { get; private set; } 17 | 18 | public static GameManagerWrapper Wrap(GameManager gameManager) => new GameManagerWrapper { 19 | GameManager = gameManager 20 | }; 21 | 22 | public override bool Equals(object obj) => GameManager.Equals(obj); 23 | 24 | public override int GetHashCode() => GameManager.GetHashCode(); 25 | 26 | public bool IsNull() => GameManager == null; 27 | 28 | #region Fields 29 | 30 | public GlobalVariablesWrapper GlobalVariables { 31 | get => GlobalVariablesWrapper.Wrap(globalVariablesField(GameManager)); 32 | set => globalVariablesField(GameManager) = value.GlobalVariables; 33 | } 34 | [WrapperField("\u030E\u0317\u0317\u030E\u030D\u0315\u0319\u0314\u0317\u030D\u030F")] 35 | private static readonly AccessTools.FieldRef globalVariablesField; 36 | 37 | public PracticeUIWrapper PracticeUI { 38 | get => PracticeUIWrapper.Wrap(practiceUIField(GameManager)); 39 | set => practiceUIField(GameManager) = value.PracticeUI; 40 | } 41 | [WrapperField("practiceUI")] 42 | private static readonly AccessTools.FieldRef practiceUIField; 43 | 44 | public StarProgressWrapper StarProgress { 45 | get => StarProgressWrapper.Wrap(starProgressField(GameManager)); 46 | set => starProgressField(GameManager) = value.StarProgress; 47 | } 48 | [WrapperField("starProgress")] 49 | private static readonly AccessTools.FieldRef starProgressField; 50 | 51 | public double SongLength { 52 | get => songLengthField(GameManager); 53 | set => songLengthField(GameManager) = value; 54 | } 55 | [WrapperField("\u031C\u0312\u0314\u0318\u0312\u030F\u0312\u031C\u0313\u030E\u0317")] 56 | private static readonly AccessTools.FieldRef songLengthField; 57 | 58 | public BasePlayerWrapper[] BasePlayers { 59 | get => basePlayersField(GameManager).Select(o => BasePlayerWrapper.Wrap(o)).ToArray(); 60 | set => basePlayersField(GameManager) = value.Select(o => o.BasePlayer).ToArray(); 61 | } 62 | [WrapperField("\u0316\u0319\u0314\u0316\u0315\u0313\u0311\u0315\u031C\u0312\u0315")] 63 | private static readonly AccessTools.FieldRef basePlayersField; 64 | 65 | public double SongTime { 66 | get => songTimeField(GameManager); 67 | set => songTimeField(GameManager) = value; 68 | } 69 | [WrapperField("\u031C\u030D\u030D\u0317\u0317\u0312\u031B\u031C\u0318\u0312\u030E")] 70 | private static readonly AccessTools.FieldRef songTimeField; 71 | 72 | public bool IsPaused { 73 | get => isPausedField(GameManager); 74 | set => isPausedField(GameManager) = value; 75 | } 76 | [WrapperField("\u031B\u0318\u030E\u0310\u0319\u030E\u0312\u031C\u031A\u030E\u0313")] 77 | private static readonly AccessTools.FieldRef isPausedField; 78 | 79 | public SongWrapper Song { 80 | get => SongWrapper.Wrap(songField(GameManager)); 81 | set => songField(GameManager) = value.Song; 82 | } 83 | [WrapperField("\u031A\u0311\u031B\u0317\u0319\u031B\u0316\u030E\u0312\u030F\u031B")] 84 | private static readonly AccessTools.FieldRef songField; 85 | 86 | public GameObject PauseMenu { 87 | get => pauseMenuField(GameManager); 88 | set => pauseMenuField(GameManager) = value; 89 | } 90 | [WrapperField("pauseMenu")] 91 | private static readonly AccessTools.FieldRef pauseMenuField; 92 | 93 | public ScoreManagerWrapper ScoreManager { 94 | get => ScoreManagerWrapper.Wrap(scoreManagerField(GameManager)); 95 | set => scoreManagerField(GameManager) = value.ScoreManager; 96 | } 97 | [WrapperField("\u0316\u031C\u031C\u0318\u0311\u0317\u0317\u030F\u0319\u0312\u030F")] 98 | private static readonly AccessTools.FieldRef scoreManagerField; 99 | 100 | public BasePlayerWrapper UnknownBasePlayer { 101 | get => BasePlayerWrapper.Wrap(unknownBasePlayerField(GameManager)); 102 | set => unknownBasePlayerField(GameManager) = value.BasePlayer; 103 | } //? It's null for me. 🤷‍ 104 | [WrapperField("\u0316\u031C\u0312\u0312\u031C\u0315\u0314\u0310\u031A\u0314\u0317")] 105 | private static readonly AccessTools.FieldRef unknownBasePlayerField; 106 | 107 | public bool ErrorWhileLoading { 108 | get => errorWhileLoadingField(GameManager); 109 | set => errorWhileLoadingField(GameManager) = value; 110 | } 111 | [WrapperField("\u0311\u0312\u0310\u031C\u0311\u031A\u031B\u030D\u0313\u030F\u030D")] 112 | private static readonly AccessTools.FieldRef errorWhileLoadingField; 113 | 114 | #endregion 115 | 116 | #region Methods 117 | 118 | /// 119 | /// Seems to create a brand new list of notes based on the chart. It probably shouldn't be called mid-game 120 | /// because performance is iffy and it has side effects. 121 | /// 122 | /// 123 | /// 124 | /// 125 | public List GetNotesFromChart(CHPlayerWrapper player, bool recomputeStars) { 126 | var notes = (ICollection)getNotesFromChartMethod(GameManager, player.CHPlayer, recomputeStars); 127 | return notes.Cast().Select(o => NoteWrapper.Wrap(o)).ToList(); 128 | } 129 | [WrapperMethod("\u0318\u030D\u031A\u031C\u031B\u0310\u031A\u030F\u030D\u0314\u031B")] 130 | private static readonly FastInvokeHandler getNotesFromChartMethod; 131 | 132 | public void OnControllerDisconnected(ControllerStatusChangedEventArgs args) => onControllerDisconnectedMethod(GameManager, args); 133 | [WrapperMethod("\u0310\u030F\u0315\u030F\u0311\u0314\u0311\u0314\u0315\u0318\u0316")] 134 | private static readonly FastInvokeHandler onControllerDisconnectedMethod; 135 | 136 | public void LoadSong() => loadSongMethod(GameManager); 137 | [WrapperMethod("\u0316\u0319\u0312\u031B\u0316\u0316\u031A\u0316\u030D\u0318\u0318")] 138 | private static readonly FastInvokeHandler loadSongMethod; 139 | 140 | #endregion 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/StarProgressWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using UnityEngine; 10 | 11 | namespace BiendeoCHLib.Wrappers { 12 | [Wrapper(typeof(StarProgress))] 13 | public struct StarProgressWrapper { 14 | public StarProgress StarProgress { get; private set; } 15 | 16 | public static StarProgressWrapper Wrap(StarProgress starProgress) => new StarProgressWrapper { 17 | StarProgress = starProgress 18 | }; 19 | 20 | public override bool Equals(object obj) => StarProgress.Equals(obj); 21 | 22 | public override int GetHashCode() => StarProgress.GetHashCode(); 23 | 24 | public bool IsNull() => StarProgress == null; 25 | 26 | #region Fields 27 | 28 | public int LastScore { 29 | get => lastScoreField(StarProgress); 30 | set => lastScoreField(StarProgress) = value; 31 | } 32 | [WrapperField("\u0310\u0318\u0311\u030F\u030F\u030F\u0315\u0313\u0316\u030E\u0317")] 33 | private static readonly AccessTools.FieldRef lastScoreField; 34 | 35 | public int BaseScore { 36 | get => baseScoreField(StarProgress); 37 | set => baseScoreField(StarProgress) = value; 38 | } 39 | [WrapperField("\u0315\u030F\u0313\u0318\u0318\u0316\u0318\u031B\u030D\u0319\u0313")] 40 | private static readonly AccessTools.FieldRef baseScoreField; 41 | 42 | // This appears to be the score required for the previous star acquired. 43 | public int ScoreToRemove { 44 | get => scoreToRemoveField(StarProgress); 45 | set => scoreToRemoveField(StarProgress) = value; 46 | } 47 | [WrapperField("\u0315\u0312\u0311\u030D\u0318\u030F\u0313\u030F\u0310\u0316\u0313")] 48 | private static readonly AccessTools.FieldRef scoreToRemoveField; 49 | 50 | // This appears to be 0 when you have zero stars, then the difference between the score from the last star to 51 | // the next star. 52 | public int ScoreToCompare { 53 | get => scoreToCompareField(StarProgress); 54 | set => scoreToCompareField(StarProgress) = value; 55 | } 56 | [WrapperField("\u0319\u0311\u0313\u031A\u030F\u0319\u031A\u030D\u030E\u0316\u031B")] 57 | private static readonly AccessTools.FieldRef scoreToCompareField; 58 | 59 | // Always 7, maybe I could...tweak this? 60 | public int MaxDisplayStar { 61 | get => maxDisplayStarField(StarProgress); 62 | set => maxDisplayStarField(StarProgress) = value; 63 | } 64 | [WrapperField("\u0319\u0318\u0316\u0316\u030E\u0316\u031A\u0318\u030E\u031C\u030F")] 65 | private static readonly AccessTools.FieldRef maxDisplayStarField; 66 | 67 | public int CurrentStar { 68 | get => currentStarField(StarProgress); 69 | set => currentStarField(StarProgress) = value; 70 | } 71 | [WrapperField("\u031C\u030E\u0316\u0312\u0317\u030E\u0310\u031C\u031B\u0310\u031A")] 72 | private static readonly AccessTools.FieldRef currentStarField; 73 | 74 | public int[] StarScores { 75 | get => starScoresField(StarProgress); 76 | set => starScoresField(StarProgress) = value; 77 | } 78 | [WrapperField("\u031A\u0312\u0318\u0314\u0318\u0318\u031C\u0317\u0311\u031C\u031A")] 79 | private static readonly AccessTools.FieldRef starScoresField; 80 | 81 | public float EndBeginning { 82 | get => endBeginningField(StarProgress); 83 | set => endBeginningField(StarProgress) = value; 84 | } 85 | [WrapperField("\u030D\u030D\u0311\u031A\u030E\u0319\u0314\u030F\u031A\u0311\u031A")] 86 | private static readonly AccessTools.FieldRef endBeginningField; 87 | 88 | public float BarMaxScale { 89 | get => barMaxScaleField(StarProgress); 90 | set => barMaxScaleField(StarProgress) = value; 91 | } 92 | [WrapperField("\u030E\u0314\u0313\u0319\u0314\u0315\u0319\u0317\u031C\u031C\u031A")] 93 | private static readonly AccessTools.FieldRef barMaxScaleField; 94 | 95 | public float EndEnd { 96 | get => endEndField(StarProgress); 97 | set => endEndField(StarProgress) = value; 98 | } 99 | [WrapperField("\u0315\u031C\u0318\u0310\u0319\u030D\u0310\u0310\u030F\u0318\u0317")] 100 | private static readonly AccessTools.FieldRef endEndField; 101 | 102 | public float EndY { 103 | get => endYField(StarProgress); 104 | set => endYField(StarProgress) = value; 105 | } 106 | [WrapperField("\u031C\u0318\u030D\u030D\u0319\u0316\u030E\u0317\u030E\u0311\u0319")] 107 | private static readonly AccessTools.FieldRef endYField; 108 | 109 | public static float[] StarMultipliers { 110 | get => (float[])starMultipliersField.GetValue(null); 111 | set => starMultipliersField.SetValue(null, value); 112 | } 113 | [WrapperField("\u0313\u030D\u0310\u030E\u0316\u030E\u030E\u0312\u0310\u0310\u0310")] 114 | private static readonly FieldInfo starMultipliersField; 115 | 116 | public Transform Transform1 { 117 | get => transform1Field(StarProgress); 118 | set => transform1Field(StarProgress) = value; 119 | } 120 | [WrapperField("\u0311\u0316\u0310\u0311\u031C\u0317\u0317\u031C\u0311\u031A\u0312")] 121 | private static readonly AccessTools.FieldRef transform1Field; 122 | 123 | public Transform Transform2 { 124 | get => transform2Field(StarProgress); 125 | set => transform2Field(StarProgress) = value; 126 | } 127 | [WrapperField("\u0317\u0318\u031B\u0311\u0312\u030D\u030F\u031B\u030F\u031B\u0318")] 128 | private static readonly AccessTools.FieldRef transform2Field; 129 | 130 | public SpriteRenderer SpriteRenderer1 { 131 | get => spriteRenderer1Field(StarProgress); 132 | set => spriteRenderer1Field(StarProgress) = value; 133 | } 134 | [WrapperField("\u0317\u0317\u0317\u0316\u0317\u031A\u0313\u031C\u0316\u031C\u030E")] 135 | private static readonly AccessTools.FieldRef spriteRenderer1Field; 136 | 137 | public SpriteRenderer SpriteRenderer2 { 138 | get => spriteRenderer2Field(StarProgress); 139 | set => spriteRenderer2Field(StarProgress) = value; 140 | } 141 | [WrapperField("\u031B\u031A\u0316\u0318\u0314\u0317\u030D\u030E\u0311\u031A\u031B")] 142 | private static readonly AccessTools.FieldRef spriteRenderer2Field; 143 | 144 | public Sprite[] SpriteArray { 145 | get => spriteArrayField(StarProgress); 146 | set => spriteArrayField(StarProgress) = value; 147 | } 148 | [WrapperField("\u0318\u030F\u0317\u030F\u0316\u0317\u030E\u030E\u0312\u0317\u0312")] 149 | private static readonly AccessTools.FieldRef spriteArrayField; 150 | 151 | #endregion 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /SplashTextEditor/Settings/Config.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Settings; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Xml.Serialization; 10 | using UnityEngine; 11 | using UnityEngine.SceneManagement; 12 | 13 | namespace SplashTextEditor.Settings { 14 | [Serializable] 15 | public class Config { 16 | public int Version; 17 | public string TweakVersion; 18 | public bool SilenceUpdates; 19 | 20 | public float ConfigX; 21 | public float ConfigY; 22 | public KeyBind ConfigKeyBind; 23 | 24 | public bool Enabled; 25 | public bool VanillaSplashMessages; 26 | public bool DragonforceOverride; 27 | public bool AprilFoolsSplashes; 28 | public float CycleTime; 29 | 30 | public List Messages; 31 | 32 | [XmlIgnore] 33 | public bool ConfigWindowEnabled; 34 | [XmlIgnore] 35 | public bool SeenChangelog; 36 | [XmlIgnore] 37 | public Action ResetSplashes; 38 | [XmlIgnore] 39 | public Action InsertMessage; 40 | [XmlIgnore] 41 | public Action DeleteMessage; 42 | [XmlIgnore] 43 | public Action ShiftUpMessage; 44 | [XmlIgnore] 45 | public Action ShiftDownMessage; 46 | [XmlIgnore] 47 | public Action PreviewMessage; 48 | [XmlIgnore] 49 | private bool wasMouseVisible; 50 | 51 | public Config() { 52 | Version = 1; 53 | TweakVersion = "0.0.0"; 54 | SilenceUpdates = false; 55 | 56 | ConfigX = 200.0f; 57 | ConfigY = 200.0f; 58 | ConfigKeyBind = new KeyBind { 59 | Key = KeyCode.F9, 60 | Ctrl = true, 61 | Alt = false, 62 | Shift = true 63 | }; 64 | 65 | Enabled = true; 66 | VanillaSplashMessages = true; 67 | DragonforceOverride = true; 68 | AprilFoolsSplashes = true; 69 | CycleTime = 15.0f; 70 | 71 | Messages = new List(); 72 | 73 | ConfigWindowEnabled = false; 74 | SeenChangelog = false; 75 | } 76 | 77 | public static Config LoadConfig(string configPath) { 78 | var configFilePath = new FileInfo(configPath); 79 | if (configFilePath.Exists) { 80 | var configString = File.ReadAllText(configFilePath.FullName); 81 | var serializer = new XmlSerializer(typeof(Config)); 82 | using (var configIn = new MemoryStream(Encoding.Unicode.GetBytes(configString))) { 83 | return serializer.Deserialize(configIn) as Config; 84 | } 85 | } else { 86 | var c = new Config(); 87 | c.SaveConfig(configPath); 88 | return c; 89 | } 90 | } 91 | 92 | public void ReloadConfig(string configPath) { 93 | var configFilePath = new FileInfo(configPath); 94 | if (configFilePath.Exists) { 95 | var configString = File.ReadAllText(configFilePath.FullName); 96 | var serializer = new XmlSerializer(typeof(Config)); 97 | using (var configIn = new MemoryStream(Encoding.Unicode.GetBytes(configString))) { 98 | var newConfig = serializer.Deserialize(configIn) as Config; 99 | ConfigKeyBind = newConfig.ConfigKeyBind; 100 | Enabled = newConfig.Enabled; 101 | VanillaSplashMessages = newConfig.VanillaSplashMessages; 102 | DragonforceOverride = newConfig.DragonforceOverride; 103 | AprilFoolsSplashes = newConfig.AprilFoolsSplashes; 104 | CycleTime = newConfig.CycleTime; 105 | Messages = newConfig.Messages; 106 | ResetSplashes(); 107 | } 108 | } 109 | } 110 | 111 | public void SaveConfig(string configPath) { 112 | var configFilePath = new FileInfo(configPath); 113 | var serializer = new XmlSerializer(typeof(Config)); 114 | using (var configOut = configFilePath.Open(FileMode.Create)) { 115 | serializer.Serialize(configOut, this); 116 | } 117 | } 118 | 119 | public void HandleInput() { 120 | if (ConfigKeyBind.IsPressed && !ConfigKeyBind.JustSet) { 121 | ConfigWindowEnabled = !ConfigWindowEnabled; 122 | if (ConfigWindowEnabled) { 123 | wasMouseVisible = Cursor.visible; 124 | Cursor.visible = true; 125 | } else { 126 | if (!wasMouseVisible) Cursor.visible = false; 127 | } 128 | } 129 | ConfigKeyBind.JustSet = false; 130 | } 131 | 132 | public void ConfigureGUI(GUIConfigurationStyles styles) { 133 | var noteLabelStyle = new GUIStyle(styles.SmallLabel) { 134 | fontStyle = FontStyle.Italic 135 | }; 136 | 137 | GUILayout.Label("Settings", styles.LargeLabel); 138 | Enabled = GUILayout.Toggle(Enabled, "Enabled", styles.Toggle); 139 | VanillaSplashMessages = GUILayout.Toggle(VanillaSplashMessages, "Vanilla Splash Messages", styles.Toggle); 140 | DragonforceOverride = GUILayout.Toggle(Enabled, "DragonForce Override", styles.Toggle); 141 | AprilFoolsSplashes = GUILayout.Toggle(Enabled, "April Fools Splashes", styles.Toggle); 142 | GUILayout.Label("Cycle Time", styles.SmallLabel); 143 | CycleTime = (float)GUILayout.HorizontalSlider(CycleTime, 0.016f, 120.0f, styles.HorizontalSlider, styles.HorizontalSliderThumb); 144 | if (float.TryParse(GUILayout.TextField(CycleTime.ToString(), styles.TextField), out float cycleTime)) CycleTime = cycleTime; 145 | if (GUILayout.Button("Randomly select new splash")) { 146 | ResetSplashes(); 147 | } 148 | 149 | GUILayout.Space(25.0f); 150 | GUILayout.Label("Configuration Keybind", styles.LargeLabel); 151 | ConfigKeyBind.ConfigureGUI(styles); 152 | 153 | GUILayout.Space(25.0f); 154 | GUILayout.Label("Splash Messages", styles.LargeLabel); 155 | GUILayout.Label("Note: Please drop out any user profiles\nbefore typing into these text areas as\ninput will be passed to the background.", noteLabelStyle); 156 | GUILayout.Space(15.0f); 157 | bool isMainMenuActive = SceneManager.GetActiveScene().name == "Main Menu"; 158 | for (int i = 0; i < Messages.Count; ++i) { 159 | GUILayout.Label($"#{i + 1}", styles.SmallLabel); 160 | Messages[i] = GUILayout.TextArea(Messages[i], styles.TextArea); 161 | if (isMainMenuActive) { 162 | if (GUILayout.Button("Preview message")) { 163 | PreviewMessage(i); 164 | } 165 | } 166 | GUILayout.BeginHorizontal(); 167 | if (i > 0 && GUILayout.Button("Shift up")) { 168 | string temp = Messages[i - 1]; 169 | Messages[i - 1] = Messages[i]; 170 | Messages[i] = temp; 171 | ShiftUpMessage(i); 172 | } 173 | if (i < Messages.Count - 1 && GUILayout.Button("Shift down")) { 174 | string temp = Messages[i + 1]; 175 | Messages[i + 1] = Messages[i]; 176 | Messages[i] = temp; 177 | ShiftDownMessage(i); 178 | } 179 | GUILayout.EndHorizontal(); 180 | GUILayout.BeginHorizontal(); 181 | if (GUILayout.Button("Delete")) { 182 | Messages.RemoveAt(i); 183 | DeleteMessage(i--); 184 | } 185 | if (GUILayout.Button("Insert new")) { 186 | Messages.Insert(i + 1, "Type your splash message here!"); 187 | InsertMessage(i); 188 | } 189 | GUILayout.EndHorizontal(); 190 | GUILayout.Space(15.0f); 191 | } 192 | if (Messages.Count == 0) { 193 | if (GUILayout.Button("Insert")) { 194 | Messages.Add("Type your splash message here!"); 195 | InsertMessage(-1); 196 | } 197 | } 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /BiendeoCHLib/Wrappers/PlayerProfileWrapper.cs: -------------------------------------------------------------------------------- 1 | using BiendeoCHLib.Wrappers.Attributes; 2 | using HarmonyLib; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace BiendeoCHLib.Wrappers { 11 | [Wrapper("\u0311\u0316\u0315\u031B\u0310\u0314\u0316\u030E\u0311\u0315\u031B")] 12 | public struct PlayerProfileWrapper { 13 | public object PlayerProfile { get; private set; } 14 | 15 | public static PlayerProfileWrapper Wrap(object playerProfile) => new PlayerProfileWrapper { 16 | PlayerProfile = playerProfile 17 | }; 18 | 19 | public override bool Equals(object obj) => PlayerProfile.Equals(obj); 20 | 21 | public override int GetHashCode() => PlayerProfile.GetHashCode(); 22 | 23 | public bool IsNull() => PlayerProfile == null; 24 | 25 | #region Fields 26 | 27 | public string PlayerName { 28 | get => playerNameField(PlayerProfile); 29 | set => playerNameField(PlayerProfile) = value; 30 | } 31 | [WrapperField("\u031A\u0311\u0312\u030D\u0315\u0310\u0311\u0316\u030D\u0311\u0312")] 32 | private static readonly AccessTools.FieldRef playerNameField; 33 | 34 | //? Different enum, there's more fields there 35 | public InstrumentType Instrument { 36 | get => (InstrumentType)instrumentField(PlayerProfile); 37 | set => instrumentField(PlayerProfile) = (sbyte)value; 38 | } 39 | [WrapperField("\u030E\u031C\u0314\u031B\u030E\u031A\u0313\u030D\u0314\u0310\u031A")] 40 | private static readonly AccessTools.FieldRef instrumentField; 41 | 42 | public Difficulty Difficulty { 43 | get => (Difficulty)difficultyField(PlayerProfile); 44 | set => difficultyField(PlayerProfile) = (sbyte)value; 45 | } 46 | [WrapperField("\u030E\u0310\u0312\u031C\u0314\u031A\u030E\u031A\u0312\u0318\u030E")] 47 | private static readonly AccessTools.FieldRef difficultyField; 48 | 49 | public NoteWrapper.Modifier Modifiers { 50 | get => (NoteWrapper.Modifier)modifiersField(PlayerProfile); 51 | set => modifiersField(PlayerProfile) = (int)value; 52 | } 53 | [WrapperField("\u030E\u0315\u0317\u030F\u0312\u0316\u0313\u0311\u030E\u030D\u0318")] 54 | private static readonly AccessTools.FieldRef modifiersField; 55 | 56 | public SongWrapper.Instrument SongInstrument { 57 | get => (SongWrapper.Instrument)songInstrumentField(PlayerProfile); 58 | set => songInstrumentField(PlayerProfile) = (int)value; 59 | } 60 | [WrapperField("\u0314\u0313\u0319\u0319\u030E\u0312\u031B\u0310\u0311\u030F\u0319")] 61 | private static readonly AccessTools.FieldRef songInstrumentField; 62 | 63 | public SongWrapper.Difficulty SongDifficulty { 64 | get => (SongWrapper.Difficulty)songDifficultyField(PlayerProfile); 65 | set => songDifficultyField(PlayerProfile) = (int)value; 66 | } 67 | [WrapperField("\u0310\u030F\u0312\u031A\u0311\u031A\u0310\u0316\u0316\u0316\u0318")] 68 | private static readonly AccessTools.FieldRef songDifficultyField; 69 | 70 | public GameSettingWrapper NoteSpeed { 71 | get => GameSettingWrapper.Wrap(noteSpeedField(PlayerProfile)); 72 | set => noteSpeedField(PlayerProfile) = value.GameSetting; 73 | } 74 | [WrapperField("\u0313\u030E\u0314\u0318\u0317\u031A\u0315\u031C\u0313\u0316\u0312")] 75 | private static readonly AccessTools.FieldRef noteSpeedField; 76 | 77 | public GameSettingWrapper Tilt { 78 | get => GameSettingWrapper.Wrap(tiltField(PlayerProfile)); 79 | set => tiltField(PlayerProfile) = value.GameSetting; 80 | } 81 | [WrapperField("\u030E\u031A\u031A\u0313\u031B\u0316\u0318\u031C\u0317\u031B\u031A")] 82 | private static readonly AccessTools.FieldRef tiltField; 83 | 84 | public GameSettingWrapper LeftyFlip { 85 | get => GameSettingWrapper.Wrap(leftyFlipField(PlayerProfile)); 86 | set => leftyFlipField(PlayerProfile) = value.GameSetting; 87 | } 88 | [WrapperField("\u0312\u0315\u0312\u0312\u0318\u0314\u0319\u0319\u031C\u030D\u031A")] 89 | private static readonly AccessTools.FieldRef leftyFlipField; 90 | 91 | public GameSettingWrapper GamepadMode { 92 | get => GameSettingWrapper.Wrap(gamepadModeField(PlayerProfile)); 93 | set => gamepadModeField(PlayerProfile) = value.GameSetting; 94 | } 95 | [WrapperField("\u0310\u031B\u0318\u0314\u0316\u031B\u0316\u0315\u031C\u0315\u0317")] 96 | private static readonly AccessTools.FieldRef gamepadModeField; 97 | 98 | public GameSettingWrapper Bot { 99 | get => GameSettingWrapper.Wrap(botField(PlayerProfile)); 100 | set => botField(PlayerProfile) = value.GameSetting; 101 | } 102 | [WrapperField("\u0310\u030E\u031C\u030D\u030E\u0311\u030F\u030D\u031A\u030E\u0317")] 103 | private static readonly AccessTools.FieldRef botField; 104 | 105 | public GameSettingWrapper DisplayName { 106 | get => GameSettingWrapper.Wrap(displayNameField(PlayerProfile)); 107 | set => displayNameField(PlayerProfile) = value.GameSetting; 108 | } 109 | [WrapperField("\u030F\u0310\u0315\u031A\u0314\u030F\u0318\u030D\u030F\u031C\u031C")] 110 | private static readonly AccessTools.FieldRef displayNameField; 111 | 112 | public GameSettingWrapper HighwayIndex { 113 | get => GameSettingWrapper.Wrap(highwayIndexField(PlayerProfile)); 114 | set => highwayIndexField(PlayerProfile) = value.GameSetting; 115 | } 116 | [WrapperField("\u031C\u030F\u0310\u031B\u0319\u031B\u031C\u030E\u030E\u0314\u030E")] 117 | private static readonly AccessTools.FieldRef highwayIndexField; 118 | 119 | public GameSettingWrapper Controller { 120 | get => GameSettingWrapper.Wrap(controllerField(PlayerProfile)); 121 | set => controllerField(PlayerProfile) = value.GameSetting; 122 | } 123 | [WrapperField("\u030D\u031B\u0318\u0310\u031B\u0310\u0318\u031C\u0313\u030E\u0311")] 124 | private static readonly AccessTools.FieldRef controllerField; 125 | 126 | public bool IsGuest { 127 | get => isGuestField(PlayerProfile); 128 | set => isGuestField(PlayerProfile) = value; 129 | } 130 | [WrapperField("\u0316\u0316\u0315\u031C\u031B\u0313\u0319\u030E\u0310\u0314\u0319")] 131 | private static readonly AccessTools.FieldRef isGuestField; 132 | 133 | #endregion 134 | 135 | #region Properties 136 | 137 | public string ControllerTypeString => (string)controllerTypeStringProperty.GetValue(PlayerProfile); 138 | [WrapperProperty("\u030F\u0319\u0312\u0310\u0316\u0314\u0318\u0318\u0310\u0312\u0312")] 139 | private static readonly PropertyInfo controllerTypeStringProperty; 140 | 141 | // An unreferenced property that just returns true. 142 | public bool True => (bool)trueProperty.GetValue(PlayerProfile); 143 | [WrapperProperty("\u031B\u0315\u0314\u0311\u0318\u0313\u030D\u0315\u031A\u030D\u0312")] 144 | private static readonly PropertyInfo trueProperty; 145 | 146 | #endregion 147 | 148 | #region Enumerations 149 | 150 | [WrapperEnum("PLACEHOLDER")] 151 | public enum ControllerType : byte { 152 | Guitar, 153 | GHLGuitar, 154 | Drums 155 | } 156 | 157 | #endregion 158 | } 159 | } 160 | --------------------------------------------------------------------------------