From b65f659bb4cb2200be6a5b2fc871e52693fa8f6d Mon Sep 17 00:00:00 2001 From: "ZackCodes.ai" Date: Wed, 11 Feb 2026 20:30:26 +0100 Subject: [PATCH] Update MAPI for Hollow Knight 1.5.12459 (Unity 6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hollow Knight received a major update on Feb 5, 2026 (build 12459) which upgraded the engine to Unity 6 (6000.0.61f1) and introduced significant changes to controller input handling, localization, and scene management. This commit updates the Modding API to compile against and work correctly with the new game binaries. Build system changes (Assembly-CSharp.csproj): - Use FrameworkPathOverride to resolve assemblies from Vanilla/ directory instead of individual HintPath references (required for Unity 6 which ships additional TeamCherry.* and System.* assemblies) - Add System.Memory NuGet package (Unity 6 uses ReadOnlySpan in Texture2D.LoadImage) - Remove Newtonsoft.Json from dependencies (now shipped with the game) - Remove mscorlib override (no longer needed with Unity 6 runtime) Localization system rewrite: - Delete Patches/Language.cs: The Language class moved from the global namespace to TeamCherry.Localization in HK 1.5. The old MonoMod patch targeting "global::Language.Language" no longer resolves. - Add Language/Language.cs: A backwards-compatible shim in the Language.Language namespace that delegates to TeamCherry.Localization via reflection, with MonoModLinkFrom to redirect existing mod references. This allows mods using Language.Language.Get() to continue working without recompilation. - Update ModHooks.cs LanguageGet to call Language.Language.GetInternal instead of the removed Patches.Language.GetInternal. Mod loading initialization (StartManager.cs): - Move mod loading bootstrap from OnScreenDebugInfo.Awake to StartManager.Awake. The OnScreenDebugInfo component was removed in HK 1.5, so it can no longer serve as the mod loading entry point. - Delete Patches/OnScreenDebugInfo.cs (component no longer exists in game). - Rewrite StartManager.Start to match the new HK 1.5 startup sequence: language verification, PlayerPrefs loading, and scene load state management. - Add deferred Menu_Title scene loading: when mod preloading is active, wait for mods to finish loading before transitioning to Menu_Title instead of using the allowSceneActivation pattern. HeroController patches updated for HK 1.5 game logic: - rb2d.velocity → rb2d.linearVelocity (Unity 6 Rigidbody2D API change) - wallSlideVibrationPlayer.Stop() → vibrationCtrl.StopWallSlide() (HK 1.5 replaced individual vibration players with HeroVibrationController) - Attack(): Add grubberfly beam (charm_35) support for wall slashes, change health check from == to >= for grubberfly beam activation on normal/up/down attacks (matches upstream game logic) - SoulGain(): String literals to nameof() for PlayerData fields - LookForQueueInput(): Remove duplicate CanDash() call - TakeDamage(): Rename flag → carefreeShouldStopDamage for clarity, string literals to nameof(), add explicit returns after death/hazard coroutine starts to match upstream flow control, fix ternary null check on damage angle - OrigDashVector(): Simplify ternary for bump velocity readability InputHandler.OnGUI rewrite: - Replace direct Cursor.visible/Cursor.lockState manipulation with calls to the game's SetCursorVisible() method. This preserves the OnCursorVisibilityChange event and proper cursor lock management through SetCursorEnabled(), fixing controller detection and connection mode switching that was broken by the old approach. GameManager patches: - Import TeamCherry.SharedUtils.Encryption (class moved in HK 1.5) - Add SetPausedState() MonoModIgnore declaration - Rewrite PauseToggleDynamicMenu to match HK 1.5 pause/unpause flow (SetPausedState calls, reordered input prevention, reduced wait times) - SceneManager.UnloadSceneAsync → SceneManager.UnloadScene (API change) SceneManager patches: - Replace DrawBlackBorders() full replacement with OnCameraAspectChanged() hook. HK 1.5 refactored border drawing into the camera aspect change handler with persistent border transforms (borderLeft/Right/Up/Down) instead of instantiating new GameObjects each time. - The ModHooks.OnDrawBlackBorders hook is preserved by collecting the border transforms after orig_OnCameraAspectChanged runs. Other patch updates: - MenuSetting.cs: Add new HK 1.5 settings enum values (SwitchFrameCap, Dithering, Noise, ControllerRumble, HudVisibility, CameraShake, NativeInput, XInput, MFi) - MenuButtonList.cs: Null-conditional on menuButtonLists to prevent NullReferenceException during early UIManager initialization - UIManager.cs: Add ADVANCED_GAMEPAD_MENU and ADVANCED_VIDEO_MENU to MainMenuState enum - TakeDamage.cs: Fix Multiplier ternary to match HK 1.5 operand order - PlayMakerUnity2DProxy.cs: Add explicit this. qualifier - ModHooks.cs: Add bounds checking on version string split to handle version formats with fewer than 4 segments Version: hollowknight.version updated to 1.5.12459 Tested on macOS (Apple M2 Pro) with Unity 6000.0.61f1: - Game launches and reaches title screen - Mod menu appears in Options with working mod list - CompassAlwaysOn and GatheringSwarmAlwaysOn load and function correctly - Controller support works (USB/Bluetooth, vibration, mode switching) - Scene transitions, pause menu, and settings menus all functional - No errors in Player.log or ModLog.txt Builds on work started in PR #164 by SFGrenade. Closes #169. --- Assembly-CSharp/Assembly-CSharp.csproj | 79 +++--------- Assembly-CSharp/Language/Language.cs | 80 ++++++++++++ Assembly-CSharp/ModHooks.cs | 10 +- Assembly-CSharp/Patches/GameManager.cs | 75 ++++++----- Assembly-CSharp/Patches/HeroController.cs | 102 +++++++-------- Assembly-CSharp/Patches/InputHandler.cs | 24 ++-- Assembly-CSharp/Patches/Language.cs | 37 ------ Assembly-CSharp/Patches/MenuButtonList.cs | 2 +- Assembly-CSharp/Patches/MenuSetting.cs | 10 +- Assembly-CSharp/Patches/OnScreenDebugInfo.cs | 40 ------ .../Patches/PlayMakerUnity2DProxy.cs | 2 +- Assembly-CSharp/Patches/SceneManager.cs | 65 ++++++---- Assembly-CSharp/Patches/StartManager.cs | 116 +++++++++++++++++- Assembly-CSharp/Patches/TakeDamage.cs | 2 +- Assembly-CSharp/Patches/UIManager.cs | 2 + hollowknight.version | 2 +- 16 files changed, 363 insertions(+), 285 deletions(-) create mode 100644 Assembly-CSharp/Language/Language.cs delete mode 100644 Assembly-CSharp/Patches/Language.cs delete mode 100644 Assembly-CSharp/Patches/OnScreenDebugInfo.cs diff --git a/Assembly-CSharp/Assembly-CSharp.csproj b/Assembly-CSharp/Assembly-CSharp.csproj index 337c16c9..3e34f4f0 100644 --- a/Assembly-CSharp/Assembly-CSharp.csproj +++ b/Assembly-CSharp/Assembly-CSharp.csproj @@ -10,6 +10,9 @@ true packages latest + true + true + $(MSBuildProjectDirectory)/../Vanilla @@ -17,7 +20,6 @@ - @@ -72,10 +74,8 @@ - + - - @@ -126,6 +126,7 @@ + @@ -141,64 +142,16 @@ - - ../Vanilla/Assembly-CSharp.dll - False - - - ..\override\mscorlib.dll - - - ../Vanilla/netstandard.dll - - - ../JsonNet/Newtonsoft.Json.dll - - - ../Vanilla/PlayMaker.dll - False - - - ../Vanilla/UnityEngine.dll - - - ../Vanilla/UnityEngine.AnimationModule.dll - - - ../Vanilla/UnityEngine.AssetBundleModule.dll - - - ../Vanilla/UnityEngine.AudioModule.dll - - - ../Vanilla/UnityEngine.CoreModule.dll - - - ../Vanilla/UnityEngine.ImageConversionModule.dll - - - ../Vanilla/UnityEngine.IMGUIModule.dll - - - ../Vanilla/UnityEngine.InputLegacyModule.dll - - - ../Vanilla/UnityEngine.JSONSerializeModule.dll - - - ../Vanilla/UnityEngine.ParticleSystemModule.dll - - - ../Vanilla/UnityEngine.Physics2DModule.dll - - - ../Vanilla/UnityEngine.TextRenderingModule.dll - - - ../Vanilla/UnityEngine.UI.dll - - - ../Vanilla/UnityEngine.UIModule.dll - + + + + + + + + + + + diff --git a/Assembly-CSharp/Language/Language.cs b/Assembly-CSharp/Language/Language.cs new file mode 100644 index 00000000..00f8bdc1 --- /dev/null +++ b/Assembly-CSharp/Language/Language.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UObject = UnityEngine.Object; +using USystemLanguage = UnityEngine.SystemLanguage; + +namespace Language; + +// for backwards compatibility +[MonoMod.MonoModLinkFrom("TeamCherry.Localization.Language")] +public static class Language +{ + static Language() + { + TllType = Type.GetType("TeamCherry.Localization.Language, TeamCherry.Localization, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + TllLoadLanguage = TllType.GetMethod("LoadLanguage", BindingFlags.Public | BindingFlags.Static, null, Array.Empty(), Array.Empty()); + TllLoadAvailableLanguages = TllType.GetMethod("LoadAvailableLanguages", BindingFlags.Public | BindingFlags.Static, null, Array.Empty(), Array.Empty()); + TllGetLanguages = TllType.GetMethod("GetLanguages", BindingFlags.Public | BindingFlags.Static, null, Array.Empty(), Array.Empty()); + TllSwitchLanguageStr = TllType.GetMethod("SwitchLanguage", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, Array.Empty()); + TllSwitchLanguageLc = TllType.GetMethod("SwitchLanguage", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(TeamCherry.Localization.LanguageCode) }, Array.Empty()); + TllGetAsset = TllType.GetMethod("GetAsset", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, Array.Empty()); + TllCurrentLanguage = TllType.GetMethod("CurrentLanguage", BindingFlags.Public | BindingFlags.Static, null, Array.Empty(), Array.Empty()); + TllGet1 = TllType.GetMethod("Get", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, Array.Empty()); + TllGetSheets = TllType.GetMethod("GetSheets", BindingFlags.Public | BindingFlags.Static, null, Array.Empty(), Array.Empty()); + TllGetKeys = TllType.GetMethod("GetKeys", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, Array.Empty()); + TllHas1 = TllType.GetMethod("Has", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, Array.Empty()); + TllHas2 = TllType.GetMethod("Has", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(string) }, Array.Empty()); + TllHasSheet = TllType.GetMethod("HasSheet", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, Array.Empty()); + TllLanguageNameToCode = TllType.GetMethod("LanguageNameToCode", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(USystemLanguage) }, Array.Empty()); + TllGet2 = TllType.GetMethod("Get", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(string) }, Array.Empty()); + } + private static readonly Type TllType; + + private static readonly MethodInfo TllLoadLanguage; + public static void LoadLanguage() => TllLoadLanguage.Invoke(null, Array.Empty()); + + private static readonly MethodInfo TllLoadAvailableLanguages; + public static void LoadAvailableLanguages() => TllLoadAvailableLanguages.Invoke(null, Array.Empty()); + + private static readonly MethodInfo TllGetLanguages; + public static string[] GetLanguages() => (string[])(TllGetLanguages.Invoke(null, Array.Empty())); + + private static readonly MethodInfo TllSwitchLanguageStr; + public static bool SwitchLanguage(string langCode) => (bool)(TllSwitchLanguageStr.Invoke(null, new object[] { langCode })); + + private static readonly MethodInfo TllSwitchLanguageLc; + public static bool SwitchLanguage(TeamCherry.Localization.LanguageCode code) => (bool)(TllSwitchLanguageLc.Invoke(null, new object[] { code })); + + private static readonly MethodInfo TllGetAsset; + public static UObject GetAsset(string name) => (UObject)(TllGetAsset.Invoke(null, new object[] { name })); + + private static readonly MethodInfo TllCurrentLanguage; + public static TeamCherry.Localization.LanguageCode CurrentLanguage() => (TeamCherry.Localization.LanguageCode)(TllCurrentLanguage.Invoke(null, Array.Empty())); + + private static readonly MethodInfo TllGet1; + public static string Get(string key) => (string)(TllGet1.Invoke(null, new object[] { key })); + + private static readonly MethodInfo TllGetSheets; + public static IEnumerable GetSheets() => (IEnumerable)(TllGetSheets.Invoke(null, Array.Empty())); + + private static readonly MethodInfo TllGetKeys; + public static IEnumerable GetKeys(string sheetTitle) => (IEnumerable)(TllGetKeys.Invoke(null, new object[] { sheetTitle })); + + private static readonly MethodInfo TllHas1; + public static bool Has(string key) => (bool)(TllHas1.Invoke(null, new object[] { key })); + + private static readonly MethodInfo TllHas2; + public static bool Has(string key, string sheet) => (bool)(TllHas2.Invoke(null, new object[] { key, sheet })); + + private static readonly MethodInfo TllHasSheet; + public static bool HasSheet(string sheet) => (bool)(TllHasSheet.Invoke(null, new object[] { sheet })); + + private static readonly MethodInfo TllLanguageNameToCode; + public static TeamCherry.Localization.LanguageCode LanguageNameToCode(USystemLanguage name) => (TeamCherry.Localization.LanguageCode)(TllLanguageNameToCode.Invoke(null, new object[] { name })); + + private static readonly MethodInfo TllGet2; + public static string GetInternal(string key, string sheetTitle) => (string)(TllGet2.Invoke(null, new object[] { key, sheetTitle })); + + public static string Get(string key, string sheetTitle) => Modding.ModHooks.LanguageGet(key, sheetTitle); +} \ No newline at end of file diff --git a/Assembly-CSharp/ModHooks.cs b/Assembly-CSharp/ModHooks.cs index 1ba7815f..0ea986d3 100644 --- a/Assembly-CSharp/ModHooks.cs +++ b/Assembly-CSharp/ModHooks.cs @@ -61,10 +61,10 @@ static ModHooks() { string[] versionNums = Constants.GAME_VERSION.Split('.'); - gameVersion.major = Convert.ToInt32(versionNums[0]); - gameVersion.minor = Convert.ToInt32(versionNums[1]); - gameVersion.revision = Convert.ToInt32(versionNums[2]); - gameVersion.package = Convert.ToInt32(versionNums[3]); + gameVersion.major = versionNums.Length > 0 ? Convert.ToInt32(versionNums[0]) : 0; + gameVersion.minor = versionNums.Length > 1 ? Convert.ToInt32(versionNums[1]) : 0; + gameVersion.revision = versionNums.Length > 2 ? Convert.ToInt32(versionNums[2]) : 0; + gameVersion.package = versionNums.Length > 3 ? Convert.ToInt32(versionNums[3]) : 0; } catch (Exception e) { @@ -225,7 +225,7 @@ internal static void LogConsole(string message, LogLevel level) /// N/A internal static string LanguageGet(string key, string sheet) { - string res = Patches.Language.GetInternal(key, sheet); + string res = Language.Language.GetInternal(key, sheet); if (LanguageGetHook == null) return res; diff --git a/Assembly-CSharp/Patches/GameManager.cs b/Assembly-CSharp/Patches/GameManager.cs index 9510c2d5..99526b73 100644 --- a/Assembly-CSharp/Patches/GameManager.cs +++ b/Assembly-CSharp/Patches/GameManager.cs @@ -8,6 +8,7 @@ using Newtonsoft.Json; using UnityEngine; using UnityEngine.SceneManagement; +using Encryption = TeamCherry.SharedUtils.Encryption; // ReSharper disable all #pragma warning disable 1591, 649, 414, 169, CS0108, CS0626 @@ -196,9 +197,7 @@ public void SaveGame(int saveSlot, Action callback) text = JsonUtility.ToJson(obj); } - bool flag = this.gameConfig.useSaveEncryption && !Platform.Current.IsFileSystemProtected; - - if (flag) + if (this.gameConfig.useSaveEncryption && !Platform.Current.IsFileSystemProtected) { string graph = Encryption.Encrypt(text); BinaryFormatter binaryFormatter = new BinaryFormatter(); @@ -578,12 +577,12 @@ public IEnumerator LoadSceneAdditive(string destScene) AsyncOperation loadop = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(destScene, LoadSceneMode.Additive); loadop.allowSceneActivation = true; yield return loadop; - yield return UnityEngine.SceneManagement.SceneManager.UnloadSceneAsync(exitingScene); + UnityEngine.SceneManagement.SceneManager.UnloadScene(exitingScene); ModHooks.OnSceneChanged(destScene); this.RefreshTilemapInfo(destScene); if (this.IsUnloadAssetsRequired(exitingScene, destScene)) { - Debug.LogFormat(this, "Unloading assets due to zone transition", new object[0]); + Debug.LogFormat(this, "Unloading assets due to zone transition", Array.Empty()); yield return Resources.UnloadUnusedAssets(); } @@ -628,48 +627,46 @@ public void OnWillActivateFirstLevel() [MonoModIgnore] public extern void SetTimeScale(float timescale); + [MonoModIgnore] + private extern void SetPausedState(bool value); + // code has been copied from PauseGameToggle public IEnumerator PauseToggleDynamicMenu(MenuScreen screen, bool allowUnpause = false) { - if (!this.TimeSlowed) + if (this.TimeSlowed) + { + yield break; + } + if (!this.playerData.GetBool(nameof(PlayerData.disablePause)) && this.gameState == GlobalEnums.GameState.PLAYING) { - if (!this.playerData.GetBool(nameof(PlayerData.disablePause)) && this.gameState == GlobalEnums.GameState.PLAYING) + this.isPaused = true; + this.ui.SetState(GlobalEnums.UIState.PAUSED); + this.SetPausedState(true); + this.SetState(GlobalEnums.GameState.PAUSED); + if (HeroController.instance != null) { - this.gameCams.StopCameraShake(); - this.inputHandler.PreventPause(); - this.inputHandler.StopUIInput(); - this.actorSnapshotPaused.TransitionTo(0f); - this.isPaused = true; - this.SetState(GlobalEnums.GameState.PAUSED); - this.ui.AudioGoToPauseMenu(0.2f); - this.ui.UIPauseToDynamicMenu(screen); - if (HeroController.instance != null) - { - HeroController.instance.Pause(); - } - this.gameCams.MoveMenuToHUDCamera(); - this.SetTimeScale(0f); - yield return new WaitForSecondsRealtime(0.8f); - this.inputHandler.AllowPause(); + HeroController.instance.Pause(); } - else if (allowUnpause && this.gameState == GlobalEnums.GameState.PAUSED) + this.gameCams.MoveMenuToHUDCamera(); + this.inputHandler.PreventPause(); + this.inputHandler.StopUIInput(); + yield return new WaitForSecondsRealtime(0.3f); + this.inputHandler.AllowPause(); + } + else if (allowUnpause && this.gameState == GlobalEnums.GameState.PAUSED) + { + this.isPaused = false; + this.inputHandler.PreventPause(); + this.ui.SetState(GlobalEnums.UIState.PLAYING); + this.SetPausedState(false); + this.SetState(GlobalEnums.GameState.PLAYING); + if (HeroController.instance != null) { - this.gameCams.ResumeCameraShake(); - this.inputHandler.PreventPause(); - this.actorSnapshotUnpaused.TransitionTo(0f); - this.isPaused = false; - this.ui.AudioGoToGameplay(0.2f); - this.ui.SetState( GlobalEnums.UIState.PLAYING); - this.SetState( GlobalEnums.GameState.PLAYING); - if (HeroController.instance != null) - { - HeroController.instance.UnPause(); - } - MenuButtonList.ClearAllLastSelected(); - this.SetTimeScale(1f); - yield return new WaitForSecondsRealtime(0.8f); - this.inputHandler.AllowPause(); + HeroController.instance.UnPause(); } + MenuButtonList.ClearAllLastSelected(); + yield return new WaitForSecondsRealtime(0.3f); + this.inputHandler.AllowPause(); } yield break; } diff --git a/Assembly-CSharp/Patches/HeroController.cs b/Assembly-CSharp/Patches/HeroController.cs index f0430b60..649580a3 100644 --- a/Assembly-CSharp/Patches/HeroController.cs +++ b/Assembly-CSharp/Patches/HeroController.cs @@ -218,17 +218,16 @@ public void Attack(AttackDirection attackDir) [MonoModReplace] public void SoulGain() { - int mpcharge = this.playerData.GetInt("MPCharge"); int num; - if (mpcharge < this.playerData.GetInt("maxMP")) + if (this.playerData.GetInt(nameof(PlayerData.MPCharge)) < this.playerData.GetInt(nameof(PlayerData.maxMP))) { num = 11; - if (this.playerData.GetBool("equippedCharm_20")) + if (this.playerData.GetBool(nameof(PlayerData.equippedCharm_20))) { num += 3; } - if (this.playerData.GetBool("equippedCharm_21")) + if (this.playerData.GetBool(nameof(PlayerData.equippedCharm_21))) { num += 8; } @@ -236,22 +235,22 @@ public void SoulGain() else { num = 6; - if (this.playerData.GetBool("equippedCharm_20")) + if (this.playerData.GetBool(nameof(PlayerData.equippedCharm_20))) { num += 2; } - if (this.playerData.GetBool("equippedCharm_21")) + if (this.playerData.GetBool(nameof(PlayerData.equippedCharm_21))) { num += 6; } } - int mpreserve = this.playerData.GetInt("MPReserve"); + int mpreserve = this.playerData.GetInt(nameof(PlayerData.MPReserve)); num = Modding.ModHooks.OnSoulGain(num); this.playerData.AddMPCharge(num); GameCameras.instance.soulOrbFSM.SendEvent("MP GAIN"); - if (this.playerData.GetInt("MPReserve") != mpreserve) + if (this.playerData.GetInt(nameof(PlayerData.MPReserve)) != mpreserve) { this.gm.soulVessel_fsm.SendEvent("MP RESERVE UP"); } @@ -446,8 +445,7 @@ private void LookForQueueInput() && this.dashQueueSteps <= this.DASH_QUEUE_STEPS && this.CanDash() && this.dashQueuing - && !ModHooks.OnDashPressed() - && this.CanDash()) + && !ModHooks.OnDashPressed()) { this.HeroDash(); } @@ -499,6 +497,9 @@ private void LookForQueueInput() [MonoModIgnore] public event HeroController.TakeDamageEvent OnTakenDamage; + [MonoModIgnore] + private HeroVibrationController vibrationCtrl; + [MonoModReplace] public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount, int hazardType) { @@ -545,7 +546,7 @@ public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount mixer.StopAllEmissionsWithTag("heroAction"); } - bool flag = false; + bool carefreeShouldStopDamage = false; if (this.carefreeShieldEquipped && hazardType == 1) { if (this.hitsSinceShielded > 7) @@ -558,58 +559,58 @@ public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount case 1: if ((float) UnityEngine.Random.Range(1, 100) <= 10f) { - flag = true; + carefreeShouldStopDamage = true; } break; case 2: if ((float) UnityEngine.Random.Range(1, 100) <= 20f) { - flag = true; + carefreeShouldStopDamage = true; } break; case 3: if ((float) UnityEngine.Random.Range(1, 100) <= 30f) { - flag = true; + carefreeShouldStopDamage = true; } break; case 4: if ((float) UnityEngine.Random.Range(1, 100) <= 50f) { - flag = true; + carefreeShouldStopDamage = true; } break; case 5: if ((float) UnityEngine.Random.Range(1, 100) <= 70f) { - flag = true; + carefreeShouldStopDamage = true; } break; case 6: if ((float) UnityEngine.Random.Range(1, 100) <= 80f) { - flag = true; + carefreeShouldStopDamage = true; } break; case 7: if ((float) UnityEngine.Random.Range(1, 100) <= 90f) { - flag = true; + carefreeShouldStopDamage = true; } break; default: - flag = false; + carefreeShouldStopDamage = false; break; } - if (flag) + if (carefreeShouldStopDamage) { this.hitsSinceShielded = 0; this.carefreeShield.SetActive(true); @@ -622,7 +623,7 @@ public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount } } - if (this.playerData.GetBool("equippedCharm_5") && this.playerData.GetInt("blockerHits") > 0 && hazardType == 1 && this.cState.focusing && !flag) + if (this.playerData.GetBool(nameof(PlayerData.equippedCharm_5)) && this.playerData.GetInt(nameof(PlayerData.blockerHits)) > 0 && hazardType == 1 && this.cState.focusing && !carefreeShouldStopDamage) { this.proxyFSM.SendEvent("HeroCtrl-TookBlockerHit"); this.audioSource.PlayOneShot(this.blockerImpact, 1f); @@ -638,7 +639,7 @@ public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount if (this.cState.wallSliding) { this.cState.wallSliding = false; - this.wallSlideVibrationPlayer.Stop(); + this.vibrationCtrl.StopWallSlide(); } if (this.cState.touchingWall) @@ -654,24 +655,24 @@ public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount if (this.cState.bouncing) { this.CancelBounce(); - this.rb2d.velocity = new Vector2(this.rb2d.velocity.x, 0f); + this.rb2d.linearVelocity = new Vector2(this.rb2d.linearVelocity.x, 0f); } if (this.cState.shroomBouncing) { this.CancelBounce(); - this.rb2d.velocity = new Vector2(this.rb2d.velocity.x, 0f); + this.rb2d.linearVelocity = new Vector2(this.rb2d.linearVelocity.x, 0f); } - if (!flag) + if (!carefreeShouldStopDamage) { this.audioCtrl.PlaySound(HeroSounds.TAKE_HIT); } damageAmount = ModHooks.AfterTakeDamage(hazardType, damageAmount); - if (!this.takeNoDamage && !this.playerData.GetBool("invinciTest")) + if (!this.takeNoDamage && !this.playerData.GetBool(nameof(PlayerData.invinciTest))) { - if (this.playerData.GetBool("overcharmed")) + if (this.playerData.GetBool(nameof(PlayerData.overcharmed))) { this.playerData.TakeHealth(damageAmount * 2); } @@ -681,9 +682,9 @@ public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount } } - if (this.playerData.GetBool("equippedCharm_3") && damageAmount > 0) + if (this.playerData.GetBool(nameof(PlayerData.equippedCharm_3)) && damageAmount > 0) { - if (this.playerData.GetBool("equippedCharm_35")) + if (this.playerData.GetBool(nameof(PlayerData.equippedCharm_35))) { this.AddMPCharge(this.GRUB_SOUL_MP_COMBO); } @@ -709,32 +710,35 @@ public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount this.OnTakenDamage(); } - if (this.playerData.GetInt("health") == 0) + if (this.playerData.GetInt(nameof(PlayerData.health)) == 0) { base.StartCoroutine(this.Die()); + return; } else if (hazardType == 2) { - base.StartCoroutine(this.DieFromHazard(HazardType.SPIKES, (!(go != null)) ? 0f : go.transform.rotation.z)); + base.StartCoroutine(this.DieFromHazard(HazardType.SPIKES, (go != null) ? go.transform.rotation.z : 0f)); + return; } else if (hazardType == 3) { base.StartCoroutine(this.DieFromHazard(HazardType.ACID, 0f)); + return; } else if (hazardType == 4) { Debug.Log("Lava death"); + return; } else if (hazardType == 5) { base.StartCoroutine(this.DieFromHazard(HazardType.PIT, 0f)); + return; } - else - { - base.StartCoroutine(this.StartRecoil(damageSide, spawnDamageEffect, damageAmount)); - } + base.StartCoroutine(this.StartRecoil(damageSide, spawnDamageEffect, damageAmount)); + return; } - else if (this.cState.invulnerable && !this.cState.hazardDeath && !this.playerData.GetBool("isInvincible")) + else if (this.cState.invulnerable && !this.cState.hazardDeath && !this.playerData.GetBool(nameof(PlayerData.isInvincible))) { if (hazardType == 2) { @@ -745,29 +749,27 @@ public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount } this.proxyFSM.SendEvent("HeroCtrl-HeroDamaged"); - if (this.playerData.GetInt("health") == 0) + if (this.playerData.GetInt(nameof(PlayerData.health)) == 0) { base.StartCoroutine(this.Die()); + return; } - else - { - this.audioCtrl.PlaySound(HeroSounds.TAKE_HIT); - base.StartCoroutine(this.DieFromHazard(HazardType.SPIKES, (!(go != null)) ? 0f : go.transform.rotation.z)); - } + this.audioCtrl.PlaySound(HeroSounds.TAKE_HIT, false); + base.StartCoroutine(this.DieFromHazard(HazardType.SPIKES, (go != null) ? go.transform.rotation.z : 0f)); + return; } else if (hazardType == 3) { damageAmount = ModHooks.AfterTakeDamage(hazardType, damageAmount); this.playerData.TakeHealth(damageAmount); this.proxyFSM.SendEvent("HeroCtrl-HeroDamaged"); - if (this.playerData.GetInt("health") == 0) + if (this.playerData.GetInt(nameof(PlayerData.health)) == 0) { base.StartCoroutine(this.Die()); + return; } - else - { - base.StartCoroutine(this.DieFromHazard(HazardType.ACID, 0f)); - } + base.StartCoroutine(this.DieFromHazard(HazardType.ACID, 0f)); + return; } else if (hazardType == 4) { @@ -842,7 +844,7 @@ private Vector2 OrigDashVector() origVector = new Vector2 ( velocity, - (!this.cState.onGround) ? BUMP_VELOCITY_DASH : BUMP_VELOCITY + this.cState.onGround ? BUMP_VELOCITY : BUMP_VELOCITY_DASH ); } else @@ -855,7 +857,7 @@ private Vector2 OrigDashVector() origVector = new Vector2 ( -velocity, - (!this.cState.onGround) ? BUMP_VELOCITY_DASH : BUMP_VELOCITY + this.cState.onGround ? BUMP_VELOCITY : BUMP_VELOCITY_DASH ); } else @@ -879,7 +881,7 @@ private void Dash() Vector2 vector = OrigDashVector(); vector = ModHooks.DashVelocityChange(vector); - rb2d.velocity = vector; + rb2d.linearVelocity = vector; dash_timer += Time.deltaTime; } diff --git a/Assembly-CSharp/Patches/InputHandler.cs b/Assembly-CSharp/Patches/InputHandler.cs index 3e5a18b4..4ed65110 100644 --- a/Assembly-CSharp/Patches/InputHandler.cs +++ b/Assembly-CSharp/Patches/InputHandler.cs @@ -21,30 +21,28 @@ public class InputHandler : global::InputHandler [MonoModIgnore] private GameManager gm; - // Reverted cursor behavior [MonoModReplace] private void OnGUI() { - Cursor.lockState = CursorLockMode.None; - if (isTitleScreenScene) + if (this.isTitleScreenScene) { - Cursor.visible = false; + SetCursorVisible(false); return; } - - if (!isMenuScene) + if (this.isMenuScene) { - ModHooks.OnCursor(gm); + SetCursorVisible(!this.controllerPressed); return; } - - if (controllerPressed) + if (!this.gm.isPaused) { - Cursor.visible = false; + SetCursorVisible(false); return; } - - Cursor.visible = true; + SetCursorVisible(!this.controllerPressed); } + + [MonoModIgnore] + private extern void SetCursorVisible(bool value); } -} \ No newline at end of file +} diff --git a/Assembly-CSharp/Patches/Language.cs b/Assembly-CSharp/Patches/Language.cs deleted file mode 100644 index 5fbd34c2..00000000 --- a/Assembly-CSharp/Patches/Language.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using MonoMod; -using UnityEngine; - -// ReSharper disable All -#pragma warning disable 1591, CS0649 - -namespace Modding.Patches -{ - [MonoModPatch("global::Language.Language")] - public static class Language - { - [MonoModIgnore] - private static Dictionary> currentEntrySheets; - - public static string GetInternal(string key, string sheetTitle) - { - if (currentEntrySheets == null || !currentEntrySheets.ContainsKey(sheetTitle)) - { - Debug.LogError($"The sheet with title \"{sheetTitle}\" does not exist!"); - return string.Empty; - } - - if (currentEntrySheets[sheetTitle].ContainsKey(key)) - { - return currentEntrySheets[sheetTitle][key]; - } - - return "#!#" + key + "#!#"; - } - - public static string Get(string key, string sheetTitle) - { - return ModHooks.LanguageGet(key, sheetTitle); - } - } -} \ No newline at end of file diff --git a/Assembly-CSharp/Patches/MenuButtonList.cs b/Assembly-CSharp/Patches/MenuButtonList.cs index 57b084e7..57c125f1 100644 --- a/Assembly-CSharp/Patches/MenuButtonList.cs +++ b/Assembly-CSharp/Patches/MenuButtonList.cs @@ -65,7 +65,7 @@ public void ClearSelectables() public void RecalculateNavigation() { - menuButtonLists.Remove(this); + menuButtonLists?.Remove(this); Start(); } diff --git a/Assembly-CSharp/Patches/MenuSetting.cs b/Assembly-CSharp/Patches/MenuSetting.cs index 51f1b298..6f989f5b 100644 --- a/Assembly-CSharp/Patches/MenuSetting.cs +++ b/Assembly-CSharp/Patches/MenuSetting.cs @@ -55,15 +55,21 @@ public enum MenuSettingType VSync, // where did 13 go MonitorSelect = 14, - FrameCap, + SwitchFrameCap, ParticleLevel, ShaderQuality, + Dithering, + Noise, // HUH???? GameLanguage = 33, GameBackerCredits, NativeAchievements, + ControllerRumble = 37, + HudVisibility = 39, + CameraShake, NativeInput, - ControllerRumble, + XInput, + MFi, // peepoHappy CustomSetting } diff --git a/Assembly-CSharp/Patches/OnScreenDebugInfo.cs b/Assembly-CSharp/Patches/OnScreenDebugInfo.cs deleted file mode 100644 index a04d41a0..00000000 --- a/Assembly-CSharp/Patches/OnScreenDebugInfo.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Threading; -using MonoMod; -using UnityEngine; - -// ReSharper disable All -#pragma warning disable 1591, CS0626 - -namespace Modding.Patches -{ - [MonoModPatch("global::OnScreenDebugInfo")] - public class OnScreenDebugInfo : global::OnScreenDebugInfo - { - private extern void orig_Awake(); - - private void Awake() - { - if (ModLoader.LoadState == ModLoader.ModLoadState.NotStarted) - { - Logger.APILogger.Log("Main menu loading"); - ModLoader.LoadState = ModLoader.ModLoadState.Started; - - GameObject obj = new GameObject(); - DontDestroyOnLoad(obj); - - // Preload reflection - new Thread(ReflectionHelper.PreloadCommonTypes).Start(); - - // NonBouncer does absolutely nothing, which makes it a good dummy to run the loader - obj.AddComponent().StartCoroutine(ModLoader.LoadModsInit(obj)); - } - else - { - // Debug log because this is the expected code path - Logger.APILogger.LogDebug($"OnScreenDebugInfo: Already begun mod loading (state {ModLoader.LoadState})"); - } - - orig_Awake(); - } - } -} \ No newline at end of file diff --git a/Assembly-CSharp/Patches/PlayMakerUnity2DProxy.cs b/Assembly-CSharp/Patches/PlayMakerUnity2DProxy.cs index 9cd2d27b..5209f962 100644 --- a/Assembly-CSharp/Patches/PlayMakerUnity2DProxy.cs +++ b/Assembly-CSharp/Patches/PlayMakerUnity2DProxy.cs @@ -23,7 +23,7 @@ public void Start() } ModHooks.OnColliderCreate(gameObject); - RefreshImplementation(); + this.RefreshImplementation(); } } } \ No newline at end of file diff --git a/Assembly-CSharp/Patches/SceneManager.cs b/Assembly-CSharp/Patches/SceneManager.cs index 677dfbbd..efc9876d 100644 --- a/Assembly-CSharp/Patches/SceneManager.cs +++ b/Assembly-CSharp/Patches/SceneManager.cs @@ -20,12 +20,14 @@ public class SceneManager : global::SceneManager [MonoModIgnore] private bool heroInfoSent; + // [MonoModIgnore] private extern void orig_Update(); [MonoModIgnore] private GameManager gm; //Added checks for null and an attempt to fix any missing references + // [MonoModReplace] private void Update() { if (this.gameplayScene) @@ -40,40 +42,51 @@ private void Update() orig_Update(); } + [MonoModIgnore] + private Transform borderLeft; + + [MonoModIgnore] + private Transform borderRight; + + [MonoModIgnore] + private Transform borderUp; + + [MonoModIgnore] + private Transform borderDown; + + // [MonoModIgnore] + private extern void orig_OnCameraAspectChanged(float aspect); + //add modhook to send the newly created borders to any mods that want them - [MonoModReplace] - private void DrawBlackBorders() + // [MonoModReplace] + private void OnCameraAspectChanged(float aspect) { - List borders = new List(); - GameObject gameObject = UnityEngine.Object.Instantiate(this.borderPrefab); - gameObject.transform.SetPosition2D(this.gm.sceneWidth + 10f, this.gm.sceneHeight / 2f); - gameObject.transform.localScale = new Vector2(20f, this.gm.sceneHeight + 40f); - borders.Add(gameObject); - - gameObject = UnityEngine.Object.Instantiate(this.borderPrefab); - gameObject.transform.SetPosition2D(-10f, this.gm.sceneHeight / 2f); - gameObject.transform.localScale = new Vector2(20f, this.gm.sceneHeight + 40f); - borders.Add(gameObject); - - gameObject = UnityEngine.Object.Instantiate(this.borderPrefab); - gameObject.transform.SetPosition2D(this.gm.sceneWidth / 2f, this.gm.sceneHeight + 10f); - gameObject.transform.localScale = new Vector2(40f + this.gm.sceneWidth, 20f); - borders.Add(gameObject); - - gameObject = UnityEngine.Object.Instantiate(this.borderPrefab); - gameObject.transform.SetPosition2D(this.gm.sceneWidth / 2f, -10f); - gameObject.transform.localScale = new Vector2(40f + this.gm.sceneWidth, 20f); - borders.Add(gameObject); + orig_OnCameraAspectChanged(aspect); - ModHooks.OnDrawBlackBorders(borders); - - foreach (var border in borders) + List borders = new List(); + if (this.borderLeft != null) + { + borders.Add(this.borderLeft.gameObject); + } + if (this.borderRight != null) + { + borders.Add(this.borderRight.gameObject); + } + if (this.borderUp != null) + { + borders.Add(this.borderUp.gameObject); + } + if (this.borderDown != null) { - UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(border, base.gameObject.scene); + borders.Add(this.borderDown.gameObject); } + ModHooks.OnDrawBlackBorders(borders); } + // [MonoModIgnore] private extern void orig_Start(); + + // [MonoModReplace] private void Start() { try diff --git a/Assembly-CSharp/Patches/StartManager.cs b/Assembly-CSharp/Patches/StartManager.cs index 01b8dbb1..be5e869f 100644 --- a/Assembly-CSharp/Patches/StartManager.cs +++ b/Assembly-CSharp/Patches/StartManager.cs @@ -1,6 +1,9 @@ -using System.Collections; +using System; +using System.Collections; +using System.Threading; using MonoMod; using UnityEngine; +using UObject = UnityEngine.Object; // ReSharper disable All #pragma warning disable 1591, CS0649 @@ -12,6 +15,36 @@ namespace Modding.Patches [MonoModPatch("global::StartManager")] public class StartManager : global::StartManager { + private bool startedPreloading = false; + + private extern void orig_Awake(); + + private void Awake() + { + if (ModLoader.LoadState == ModLoader.ModLoadState.NotStarted) + { + Logger.APILogger.Log("Main menu loading"); + startedPreloading = true; + ModLoader.LoadState = ModLoader.ModLoadState.Started; + + GameObject obj = new GameObject(); + DontDestroyOnLoad(obj); + + // Preload reflection + new Thread(ReflectionHelper.PreloadCommonTypes).Start(); + + // NonBouncer does absolutely nothing, which makes it a good dummy to run the loader + obj.AddComponent().StartCoroutine(ModLoader.LoadModsInit(obj)); + } + else + { + // Debug log because this is the expected code path + Logger.APILogger.LogDebug($"StartManager: Already begun mod loading (state {ModLoader.LoadState})"); + } + + orig_Awake(); + } + [MonoModIgnore] private bool confirmedLanguage; @@ -30,11 +63,18 @@ public class StartManager : global::StartManager [MonoModIgnore] private extern IEnumerator LanguageSettingDone(); + [MonoModReplace] private IEnumerator Start() { this.controllerImage.sprite = this.GetControllerSpriteForPlatform(this.platform); - AsyncOperation loadOperation = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Menu_Title"); - loadOperation.allowSceneActivation = false; + + AsyncOperation loadOperation = null; + if (!startedPreloading) + { + loadOperation = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Menu_Title"); + loadOperation.allowSceneActivation = false; + } + Platform.Current.SetSceneLoadState(true, false); bool showLanguageSelect = !this.CheckIsLanguageSet(); if (showLanguageSelect && Platform.Current.ShowLanguageSelect) { @@ -46,14 +86,78 @@ private IEnumerator Start() yield return base.StartCoroutine(this.LanguageSettingDone()); } - + TeamCherry.Localization.LanguageCode currentLanguage = Language.Language.CurrentLanguage(); + while (!Platform.Current.IsSharedDataMounted) + { + yield return null; + } + bool flag = false; + string text; + if (TeamCherry.Localization.LocalizationProjectSettings.TryGetSavedLanguageCode(out text)) + { + TeamCherry.Localization.LanguageCode languageEnum = TeamCherry.Localization.LocalizationSettings.GetLanguageEnum(text); + if (currentLanguage != languageEnum) + { + flag = true; + } + } + if (flag) + { + Language.Language.LoadLanguage(); + ChangeFontByLanguage[] array = UObject.FindObjectsByType(FindObjectsSortMode.None); + for (int i = 0; i < array.Length; i++) + { + array[i].SetFont(); + } + SetTextMeshProGameText[] componentsInChildren = base.GetComponentsInChildren(true); + for (int i = 0; i < componentsInChildren.Length; i++) + { + componentsInChildren[i].UpdateText(); + } + LogoLanguage[] componentsInChildren2 = base.GetComponentsInChildren(true); + for (int i = 0; i < componentsInChildren2.Length; i++) + { + componentsInChildren2[i].SetSprite(); + } + } this.startManagerAnimator.SetBool("WillShowControllerNotice", false); this.startManagerAnimator.SetBool("WillShowQuote", true); StandaloneLoadingSpinner loadSpinner = UnityEngine.Object.Instantiate(this.loadSpinnerPrefab); loadSpinner.Setup(null); - loadOperation.allowSceneActivation = true; - yield return loadOperation; + bool didWaitForPlayerPrefs = false; + while (!Platform.Current.IsPlayerPrefsLoaded) + { + if (!didWaitForPlayerPrefs) + { + didWaitForPlayerPrefs = true; + Debug.LogFormat("Waiting for PlayerPrefs load...", Array.Empty()); + } + yield return null; + } + if (!didWaitForPlayerPrefs) + { + Debug.LogFormat("Didn't need to wait for PlayerPrefs load.", Array.Empty()); + } + else + { + Debug.LogFormat("Finished waiting for PlayerPrefs load.", Array.Empty()); + } + Platform.Current.SetSceneLoadState(true, true); + if (!startedPreloading) + { + loadOperation.allowSceneActivation = true; + yield return loadOperation; + } + else + { + // Wait for mod loading to finish before transitioning + while ((ModLoader.LoadState & ModLoader.ModLoadState.Loaded) == 0) + { + yield return null; + } + yield return UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Menu_Title"); + } yield break; } } diff --git a/Assembly-CSharp/Patches/TakeDamage.cs b/Assembly-CSharp/Patches/TakeDamage.cs index fdd497ae..5a55ff7e 100644 --- a/Assembly-CSharp/Patches/TakeDamage.cs +++ b/Assembly-CSharp/Patches/TakeDamage.cs @@ -22,7 +22,7 @@ public override void OnEnter() MagnitudeMultiplier = this.MagnitudeMultiplier.Value, MoveAngle = this.MoveAngle.Value, MoveDirection = this.MoveDirection.Value, - Multiplier = ((!this.Multiplier.IsNone) ? this.Multiplier.Value : 1f), + Multiplier = (this.Multiplier.IsNone ? 1f : this.Multiplier.Value), SpecialType = (SpecialTypes) this.SpecialType.Value, IsExtraDamage = false }; diff --git a/Assembly-CSharp/Patches/UIManager.cs b/Assembly-CSharp/Patches/UIManager.cs index 0de510dd..b79be999 100644 --- a/Assembly-CSharp/Patches/UIManager.cs +++ b/Assembly-CSharp/Patches/UIManager.cs @@ -194,10 +194,12 @@ public enum MainMenuState MAIN_MENU, OPTIONS_MENU, GAMEPAD_MENU, + ADVANCED_GAMEPAD_MENU, KEYBOARD_MENU, SAVE_PROFILES, AUDIO_MENU, VIDEO_MENU, + ADVANCED_VIDEO_MENU, EXIT_PROMPT, OVERSCAN_MENU, GAME_OPTIONS_MENU, diff --git a/hollowknight.version b/hollowknight.version index 8a7a4d76..68c21b76 100644 --- a/hollowknight.version +++ b/hollowknight.version @@ -1 +1 @@ -1.5.78.11833 \ No newline at end of file +1.5.12459 \ No newline at end of file