Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PickYourSpirit v1.0.0
PickYourSpirit.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("PickYourSpirit")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+0e75840dcb525b730e1f13ec15aadf9e3d4c5975")] [assembly: AssemblyProduct("PickYourSpirit")] [assembly: AssemblyTitle("PickYourSpirit")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PickYourSpirit { [HarmonyPatch] internal static class ButtonPatches { private static FieldRef<Character, bool> s_secondaryAttack; private static bool s_errorLogged; private static bool Prepare() { if (s_secondaryAttack != null) { return true; } try { s_secondaryAttack = AccessTools.FieldRefAccess<Character, bool>("m_secondaryAttack"); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("Middle mouse can't pick spirits: the game's press field wasn't found (a game update?): " + ex.Message)); return false; } } [HarmonyPatch(typeof(Player), "SetControls")] [HarmonyPostfix] private static void SetControls(Player __instance) { try { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && s_secondaryAttack.Invoke((Character)(object)__instance)) { ItemData currentWeapon = ((Humanoid)__instance).GetCurrentWeapon(); if (SpiritCaller.IsSpiritCaller(currentWeapon)) { SpiritCaller.Press(currentWeapon); } } } catch (Exception arg) { if (!s_errorLogged) { s_errorLogged = true; Plugin.Log.LogError((object)$"Picking the next spirit failed (logged once): {arg}"); } } } } [HarmonyPatch] internal static class CastPatches { private static bool s_errorLogged; [HarmonyPatch(typeof(SpawnAbility), "Setup")] [HarmonyPrefix] private static void Setup(SpawnAbility __instance, Character owner, ItemData item) { try { string pick = SpiritCaller.Pick; if (pick != null && !((Object)(object)owner == (Object)null) && !((Object)(object)owner != (Object)(object)Player.m_localPlayer) && SpiritCaller.IsSpiritCaller(item)) { GameObject[] spawnPrefab = __instance.m_spawnPrefab; int num = SpiritRules.Resolve(pick, SpiritCaller.Names(spawnPrefab)); if (num >= 0) { __instance.m_spawnPrefab = (GameObject[])(object)new GameObject[1] { spawnPrefab[num] }; } } } catch (Exception arg) { if (!s_errorLogged) { s_errorLogged = true; Plugin.Log.LogError((object)$"Applying the picked spirit failed (logged once; the cast stays random): {arg}"); } } } } [HarmonyPatch] internal static class HintPatches { private static KeyHints s_hints; private static bool s_relabelled; private static bool s_off; [HarmonyPatch(typeof(KeyHints), "UpdateHints")] [HarmonyPostfix] private static void UpdateHints(KeyHints __instance) { if (s_off) { return; } try { if ((Object)(object)__instance != (Object)(object)s_hints) { s_hints = __instance; s_relabelled = false; } Player localPlayer = Player.m_localPlayer; bool flag = (Object)(object)localPlayer != (Object)null && (Object)(object)__instance.m_combatHints != (Object)null && __instance.m_combatHints.activeSelf && SpiritCaller.IsSpiritCaller(((Humanoid)localPlayer).GetCurrentWeapon()); if (flag != s_relabelled) { Relabel(__instance, flag); } if (flag && !s_off) { __instance.m_secondaryAttackKB.SetActive(true); __instance.m_secondaryAttackGP.SetActive(true); } } catch (Exception arg) { TurnOff(__instance, $"The 'Next spirit' key hint failed; the hint is left to the game from now on: {arg}"); } } private static void Relabel(KeyHints hints, bool spirit) { if (!Labels(hints, out var keyboard, out var gamepad)) { TurnOff(hints, "The key hint's label wasn't found (a game update?): the hint is left to the game"); return; } SetText(keyboard, spirit ? "Next spirit" : "$settings_secondaryattack"); SetText(gamepad, spirit ? "Next spirit <mspace=0.6em> $KEY_SecondaryAttack</mspace>" : "$settings_secondaryattack <mspace=0.6em> $KEY_SecondaryAttack</mspace>"); s_relabelled = spirit; } private static bool Labels(KeyHints hints, out TMP_Text keyboard, out TMP_Text gamepad) { Transform val = (((Object)(object)hints.m_secondaryAttackKB != (Object)null) ? hints.m_secondaryAttackKB.transform.Find("Text") : null); keyboard = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TMP_Text>() : null); gamepad = (((Object)(object)hints.m_secondaryAttackGP != (Object)null) ? hints.m_secondaryAttackGP.GetComponent<TMP_Text>() : null); if ((Object)(object)keyboard != (Object)null) { return (Object)(object)gamepad != (Object)null; } return false; } private static void SetText(TMP_Text text, string value) { Localization.instance.RemoveTextFromCache(text); text.text = value; Localization.instance.Localize(text.transform); } private static void TurnOff(KeyHints hints, string message) { s_off = true; Plugin.Log.LogWarning((object)message); if (!s_relabelled) { return; } try { if (Labels(hints, out var keyboard, out var gamepad)) { SetText(keyboard, "$settings_secondaryattack"); SetText(gamepad, "$settings_secondaryattack <mspace=0.6em> $KEY_SecondaryAttack</mspace>"); } } catch (Exception) { } s_relabelled = false; } } [BepInPlugin("algo7.pickyourspirit", "PickYourSpirit", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "algo7.pickyourspirit"; public const string Name = "PickYourSpirit"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; if (Application.isBatchMode) { Log.LogInfo((object)"PickYourSpirit loaded (v1.0.0): dedicated server, nothing to do"); return; } Harmony harmony = new Harmony("algo7.pickyourspirit"); if (!TryPatch(harmony, typeof(CastPatches), "cast hook")) { Log.LogError((object)"Picking spirits is off: without the cast hook a pick would do nothing"); return; } TryPatch(harmony, typeof(ButtonPatches), "middle mouse hook"); if (!TryPatch(harmony, typeof(HintPatches), "key hint hook")) { Log.LogWarning((object)"No 'Next spirit' key hint (picking still works)"); } SceneManager.sceneLoaded += OnSceneLoaded; Log.LogInfo((object)"PickYourSpirit loaded (v1.0.0)"); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { SpiritCaller.Pick = null; } private static bool TryPatch(Harmony harmony, Type patches, string what) { try { harmony.PatchAll(patches); return true; } catch (Exception arg) { Log.LogError((object)$"Could not install the {what}: {arg}"); return false; } } } internal static class SpiritCaller { public const string ItemName = "$item_staff_spiritcaller"; public static string Pick; private static bool s_warnedNoList; public static bool IsSpiritCaller(ItemData item) { if (item != null && item.m_shared != null) { return item.m_shared.m_name == "$item_staff_spiritcaller"; } return false; } public static GameObject[] SpawnList(ItemData staff) { GameObject val = staff?.m_shared?.m_attack?.m_attackProjectile; if ((Object)(object)val == (Object)null) { return null; } SpawnAbility component = val.GetComponent<SpawnAbility>(); if (!((Object)(object)component != (Object)null)) { return null; } return component.m_spawnPrefab; } public static List<string> Names(GameObject[] list) { List<string> list2 = new List<string>(); if (list == null) { return list2; } foreach (GameObject val in list) { list2.Add(((Object)(object)val != (Object)null) ? ((Object)val).name : null); } return list2; } public static void Press(ItemData staff) { GameObject[] list = SpawnList(staff); List<string> list2 = SpiritRules.Choices(Names(list)); if (list2.Count == 0) { if (!s_warnedNoList) { s_warnedNoList = true; Plugin.Log.LogWarning((object)"The Spirit Caller has no spirit list (a game update?): its spirits stay random"); } } else { Pick = SpiritRules.Next(Pick, list2); if (!((Object)(object)MessageHud.instance == (Object)null)) { MessageHud.instance.ShowMessage((MessageType)2, SpiritRules.Message(ShownName(list, Pick)), 0, (Sprite)null, false, false); } } } private static string ShownName(GameObject[] list, string pick) { if (pick == null) { return null; } string text = ""; foreach (GameObject val in list) { if (!((Object)(object)val == (Object)null) && !(((Object)val).name != pick)) { text = NameOf(val); break; } } string text2 = SpiritRules.AnimalOf(pick); string text3 = (((Object)(object)ZNetScene.instance != (Object)null) ? NameOf(ZNetScene.instance.GetPrefab(text2)) : ""); return SpiritRules.FirstName(text, text3, text2, pick); } private static string NameOf(GameObject prefab) { Character val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Character>() : null); if ((Object)(object)val == (Object)null || string.IsNullOrEmpty(val.m_name)) { return ""; } return Localization.instance.Localize(val.m_name); } } internal static class SpiritRules { public const string HintLabel = "Next spirit"; public const string GamepadKey = " <mspace=0.6em> $KEY_SecondaryAttack</mspace>"; public const string VanillaKeyboardHint = "$settings_secondaryattack"; public const string VanillaGamepadHint = "$settings_secondaryattack <mspace=0.6em> $KEY_SecondaryAttack</mspace>"; public const string SpiritGamepadHint = "Next spirit <mspace=0.6em> $KEY_SecondaryAttack</mspace>"; private const string SpiritEnding = "_spiritcaller"; public static List<string> Choices(IEnumerable<string> names) { List<string> list = new List<string>(); if (names == null) { return list; } foreach (string name in names) { if (!string.IsNullOrEmpty(name) && !list.Contains(name)) { list.Add(name); } } return list; } public static string Next(string pick, IReadOnlyList<string> choices) { if (choices == null || choices.Count == 0) { return null; } if (pick == null) { return choices[0]; } int num = IndexOf(choices, pick); if (num < 0 || num + 1 >= choices.Count) { return null; } return choices[num + 1]; } public static int Resolve(string pick, IReadOnlyList<string> names) { if (pick == null || names == null) { return -1; } return IndexOf(names, pick); } public static string Message(string spirit) { return "Next spirit: " + (spirit ?? "Random"); } public static string AnimalOf(string spiritPrefab) { if (spiritPrefab == null || spiritPrefab.Length <= "_spiritcaller".Length) { return spiritPrefab; } if (!spiritPrefab.EndsWith("_spiritcaller", StringComparison.OrdinalIgnoreCase)) { return spiritPrefab; } return spiritPrefab.Substring(0, spiritPrefab.Length - "_spiritcaller".Length); } public static string FirstName(params string[] names) { for (int i = 0; i < names.Length; i++) { string text = names[i]?.Trim(); if (!string.IsNullOrEmpty(text) && (!text.StartsWith("[", StringComparison.Ordinal) || !text.EndsWith("]", StringComparison.Ordinal))) { return text; } } return null; } private static int IndexOf(IReadOnlyList<string> names, string name) { for (int i = 0; i < names.Count; i++) { if (string.Equals(names[i], name, StringComparison.Ordinal)) { return i; } } return -1; } } internal static class PluginInfo { public const string Version = "1.0.0"; } }